aboutsummaryrefslogtreecommitdiff
path: root/jslinux-2019-12-21/tinyemu-2019-12-21
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2025-12-24 19:49:57 -0500
committerMitchell Riedstra <mitch@riedstra.dev>2025-12-24 19:49:57 -0500
commit939ac4319cb047a37ba46f84eff81948063f6954 (patch)
tree5112cf8aad73125a13f5b52c0290a7f26f948b52 /jslinux-2019-12-21/tinyemu-2019-12-21
parent3a1b5ba15b89c907f9bf66a0761ffdd73b32208b (diff)
downloadunixv4-939ac4319cb047a37ba46f84eff81948063f6954.tar.gz
unixv4-939ac4319cb047a37ba46f84eff81948063f6954.tar.xz
Add working webpage for unix v4
Diffstat (limited to 'jslinux-2019-12-21/tinyemu-2019-12-21')
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/Changelog45
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/MIT-LICENSE.txt19
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/Makefile135
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/Makefile.js65
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/VERSION1
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/aes.c1321
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/aes.h49
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/block_net.c531
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/build_filelist.c311
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/cutils.c120
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/cutils.h194
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fbuf.h22
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs.c104
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs.h211
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs_disk.c659
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs_net.c2910
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs_utils.c370
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs_utils.h95
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c625
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.h93
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/ide.c835
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/ide.h32
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/iomem.c264
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/iomem.h148
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/js/lib.js280
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c349
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/json.c464
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/json.h132
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/list.h94
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/machine.c640
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/machine.h196
-rwxr-xr-xjslinux-2019-12-21/tinyemu-2019-12-21/netinit.sh43
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/pci.c588
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/pci.h81
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/pckbd.c342
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/ps2.c489
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/ps2.h34
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/readme.txt204
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu.c1377
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu.h118
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_fp_template.h304
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_priv.h293
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_template.h1739
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c1053
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/sdl.c275
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/sha256.c341
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/sha256.h40
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/simplefb.c127
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/bootp.c314
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/bootp.h122
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/cksum.c139
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/debug.h34
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/if.c209
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/if.h25
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip.h253
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_icmp.c351
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_icmp.h161
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_input.c685
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_output.c172
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/libslirp.h56
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/main.h46
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/mbuf.c218
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/mbuf.h127
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/misc.c403
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/misc.h73
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/sbuf.c181
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/sbuf.h30
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp.c828
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp.h313
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp_config.h188
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/socket.c722
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/socket.h95
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp.h164
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_input.c1487
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_output.c492
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_subr.c915
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_timer.c292
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_timer.h127
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_var.h161
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcpip.h77
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tftp.h43
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/udp.c386
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/slirp/udp.h86
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/softfp.c87
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/softfp.h181
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/softfp_template.h1129
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/softfp_template_icvt.h171
-rwxr-xr-xjslinux-2019-12-21/tinyemu-2019-12-21/splitimgbin0 -> 21968 bytes
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.c102
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.d1
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.obin0 -> 13592 bytes
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/temu.c835
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/vga.c804
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/virtio.c2650
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/virtio.h146
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/vmmouse.c162
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/x86_cpu.c96
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/x86_cpu.h70
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/x86_machine.c2569
99 files changed, 37740 insertions, 0 deletions
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/Changelog b/jslinux-2019-12-21/tinyemu-2019-12-21/Changelog
new file mode 100644
index 0000000..540a05f
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/Changelog
@@ -0,0 +1,45 @@
+2019-12-21:
+
+- added complete JSLinux demo
+- RISC-V: added initrd support
+- RISC-V: fixed FMIN/FMAX instructions
+
+2018-09-23:
+
+- added support for separate RISC-V BIOS and kernel
+
+2018-09-15:
+
+- renamed to TinyEMU (temu)
+- single executable for all emulated machines
+
+2018-08-29:
+
+- compilation fixes
+
+2017-08-06:
+
+- added JSON configuration file
+- added graphical display with SDL
+- added VirtIO input support
+- added PCI bus and VirtIO PCI support
+- x86: added IDE, PS/2, vmmouse and VGA devices
+- added user mode network interface
+
+2017-06-10:
+
+- RISCV: avoid unnecessary kernel patches
+- x86: accept standard kernel images
+
+2017-05-25:
+
+- RISCV: faster emulation (1.4x)
+- Support of user level ISA version 2.2 and priviledged architecture
+ version 1.10
+- added small x86 emulator (x86emu) based on KVM
+- modified the fs_net network protocol to match the vfsync protocol
+- handle console resize
+- JS emulator:
+ - added scrollbar in terminal
+ - added file import and export
+ - added copy/paste support
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/MIT-LICENSE.txt b/jslinux-2019-12-21/tinyemu-2019-12-21/MIT-LICENSE.txt
new file mode 100644
index 0000000..3f38cbe
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/MIT-LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2016-2017 Fabrice Bellard
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/Makefile b/jslinux-2019-12-21/tinyemu-2019-12-21/Makefile
new file mode 100644
index 0000000..390ae37
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/Makefile
@@ -0,0 +1,135 @@
+#
+# TinyEMU
+#
+# Copyright (c) 2016-2018 Fabrice Bellard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+# if set, network filesystem is enabled. libcurl and libcrypto
+# (openssl) must be installed.
+CONFIG_FS_NET=y
+# SDL support (optional)
+CONFIG_SDL=y
+# if set, compile the 128 bit emulator. Note: the 128 bit target does
+# not compile if gcc does not support the int128 type (32 bit hosts).
+CONFIG_INT128=y
+# build x86 emulator
+CONFIG_X86EMU=y
+# win32 build (not usable yet)
+#CONFIG_WIN32=y
+# user space network redirector
+CONFIG_SLIRP=y
+
+ifdef CONFIG_WIN32
+CROSS_PREFIX=i686-w64-mingw32-
+EXE=.exe
+else
+CROSS_PREFIX=
+EXE=
+endif
+CC=$(CROSS_PREFIX)gcc
+STRIP=$(CROSS_PREFIX)strip
+CFLAGS=-O2 -Wall -g -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -MMD
+CFLAGS+=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
+LDFLAGS=
+
+bindir=/usr/local/bin
+INSTALL=install
+
+PROGS+= temu$(EXE)
+ifndef CONFIG_WIN32
+ifdef CONFIG_FS_NET
+PROGS+=build_filelist splitimg
+endif
+endif
+
+all: $(PROGS)
+
+EMU_OBJS:=virtio.o pci.o fs.o cutils.o iomem.o simplefb.o \
+ json.o machine.o temu.o
+
+ifdef CONFIG_SLIRP
+CFLAGS+=-DCONFIG_SLIRP
+EMU_OBJS+=$(addprefix slirp/, bootp.o ip_icmp.o mbuf.o slirp.o tcp_output.o cksum.o ip_input.o misc.o socket.o tcp_subr.o udp.o if.o ip_output.o sbuf.o tcp_input.o tcp_timer.o)
+endif
+
+ifndef CONFIG_WIN32
+EMU_OBJS+=fs_disk.o
+EMU_LIBS=-lrt
+endif
+ifdef CONFIG_FS_NET
+CFLAGS+=-DCONFIG_FS_NET
+EMU_OBJS+=fs_net.o fs_wget.o fs_utils.o block_net.o
+EMU_LIBS+=-lcurl -lcrypto
+ifdef CONFIG_WIN32
+EMU_LIBS+=-lwsock32
+endif # CONFIG_WIN32
+endif # CONFIG_FS_NET
+ifdef CONFIG_SDL
+EMU_LIBS+=-lSDL
+EMU_OBJS+=sdl.o
+CFLAGS+=-DCONFIG_SDL
+ifdef CONFIG_WIN32
+LDFLAGS+=-mwindows
+endif
+endif
+
+EMU_OBJS+=riscv_machine.o softfp.o riscv_cpu32.o riscv_cpu64.o
+ifdef CONFIG_INT128
+CFLAGS+=-DCONFIG_RISCV_MAX_XLEN=128
+EMU_OBJS+=riscv_cpu128.o
+else
+CFLAGS+=-DCONFIG_RISCV_MAX_XLEN=64
+endif
+ifdef CONFIG_X86EMU
+CFLAGS+=-DCONFIG_X86EMU
+EMU_OBJS+=x86_cpu.o x86_machine.o ide.o ps2.o vmmouse.o pckbd.o vga.o
+endif
+
+temu$(EXE): $(EMU_OBJS)
+ $(CC) $(LDFLAGS) -o $@ $^ $(EMU_LIBS)
+
+riscv_cpu32.o: riscv_cpu.c
+ $(CC) $(CFLAGS) -DMAX_XLEN=32 -c -o $@ $<
+
+riscv_cpu64.o: riscv_cpu.c
+ $(CC) $(CFLAGS) -DMAX_XLEN=64 -c -o $@ $<
+
+riscv_cpu128.o: riscv_cpu.c
+ $(CC) $(CFLAGS) -DMAX_XLEN=128 -c -o $@ $<
+
+build_filelist: build_filelist.o fs_utils.o cutils.o
+ $(CC) $(LDFLAGS) -o $@ $^ -lm
+
+splitimg: splitimg.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+install: $(PROGS)
+ $(STRIP) $(PROGS)
+ $(INSTALL) -m755 $(PROGS) "$(DESTDIR)$(bindir)"
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+clean:
+ rm -f *.o *.d *~ $(PROGS) slirp/*.o slirp/*.d slirp/*~
+
+-include $(wildcard *.d)
+-include $(wildcard slirp/*.d)
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/Makefile.js b/jslinux-2019-12-21/tinyemu-2019-12-21/Makefile.js
new file mode 100644
index 0000000..3d76f77
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/Makefile.js
@@ -0,0 +1,65 @@
+#
+# TinyEMU emulator
+#
+# Copyright (c) 2016-2018 Fabrice Bellard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+# Build the Javascript version of TinyEMU
+EMCC=emcc
+EMCFLAGS=-O2 --llvm-opts 2 -Wall -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -MMD -fno-strict-aliasing -DCONFIG_FS_NET
+#EMCFLAGS+=-Werror
+EMLDFLAGS=-O3 --memory-init-file 0 --closure 0 -s NO_EXIT_RUNTIME=1 -s NO_FILESYSTEM=1 -s "EXPORTED_FUNCTIONS=['_console_queue_char','_vm_start','_fs_import_file','_display_key_event','_display_mouse_event','_display_wheel_event','_net_write_packet','_net_set_carrier']" -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]' -s BINARYEN_TRAP_MODE=clamp --js-library js/lib.js
+EMLDFLAGS_ASMJS:=$(EMLDFLAGS) -s WASM=0
+EMLDFLAGS_WASM:=$(EMLDFLAGS) -s WASM=1 -s TOTAL_MEMORY=67108864 -s ALLOW_MEMORY_GROWTH=1
+
+PROGS=js/riscvemu32.js js/riscvemu32-wasm.js js/riscvemu64.js js/riscvemu64-wasm.js
+
+all: $(PROGS)
+
+JS_OBJS=jsemu.js.o softfp.js.o virtio.js.o fs.js.o fs_net.js.o fs_wget.js.o fs_utils.js.o simplefb.js.o pci.js.o json.js.o block_net.js.o
+JS_OBJS+=iomem.js.o cutils.js.o aes.js.o sha256.js.o
+
+RISCVEMU64_OBJS=$(JS_OBJS) riscv_cpu64.js.o riscv_machine.js.o machine.js.o
+RISCVEMU32_OBJS=$(JS_OBJS) riscv_cpu32.js.o riscv_machine.js.o machine.js.o
+
+js/riscvemu64.js: $(RISCVEMU64_OBJS) js/lib.js
+ $(EMCC) $(EMLDFLAGS_ASMJS) -o $@ $(RISCVEMU64_OBJS)
+
+js/riscvemu32.js: $(RISCVEMU32_OBJS) js/lib.js
+ $(EMCC) $(EMLDFLAGS_ASMJS) -o $@ $(RISCVEMU32_OBJS)
+
+js/riscvemu64-wasm.js: $(RISCVEMU64_OBJS) js/lib.js
+ $(EMCC) $(EMLDFLAGS_WASM) -o $@ $(RISCVEMU64_OBJS)
+
+js/riscvemu32-wasm.js: $(RISCVEMU32_OBJS) js/lib.js
+ $(EMCC) $(EMLDFLAGS_WASM) -o $@ $(RISCVEMU32_OBJS)
+
+riscv_cpu32.js.o: riscv_cpu.c
+ $(EMCC) $(EMCFLAGS) -DMAX_XLEN=32 -DCONFIG_RISCV_MAX_XLEN=32 -c -o $@ $<
+
+riscv_cpu64.js.o: riscv_cpu.c
+ $(EMCC) $(EMCFLAGS) -DMAX_XLEN=64 -DCONFIG_RISCV_MAX_XLEN=64 -c -o $@ $<
+
+
+%.js.o: %.c
+ $(EMCC) $(EMCFLAGS) -c -o $@ $<
+
+-include $(wildcard *.d)
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/VERSION b/jslinux-2019-12-21/tinyemu-2019-12-21/VERSION
new file mode 100644
index 0000000..bd16728
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/VERSION
@@ -0,0 +1 @@
+2019-12-21
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/aes.c b/jslinux-2019-12-21/tinyemu-2019-12-21/aes.c
new file mode 100644
index 0000000..ac8af6d
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/aes.c
@@ -0,0 +1,1321 @@
+/**
+ *
+ * aes.c - integrated in QEMU by Fabrice Bellard from the OpenSSL project.
+ */
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <string.h>
+#include <inttypes.h>
+#include "aes.h"
+
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+
+#include <assert.h>
+
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+
+#define MAXKC (256/32)
+#define MAXKB (256/8)
+#define MAXNR 14
+
+/* This controls loop-unrolling in aes_core.c */
+#undef FULL_UNROLL
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+static const u32 Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static const u32 Te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+static const u32 Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u32 Td4[256] = {
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+static const u32 rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key) {
+
+ u32 *rk;
+ int i = 0;
+ u32 temp;
+
+ if (!userKey || !key)
+ return -1;
+ if (bits != 128 && bits != 192 && bits != 256)
+ return -2;
+
+ rk = key->rd_key;
+
+ if (bits==128)
+ key->rounds = 10;
+ else if (bits==192)
+ key->rounds = 12;
+ else
+ key->rounds = 14;
+
+ rk[0] = GETU32(userKey );
+ rk[1] = GETU32(userKey + 4);
+ rk[2] = GETU32(userKey + 8);
+ rk[3] = GETU32(userKey + 12);
+ if (bits == 128) {
+ while (1) {
+ temp = rk[3];
+ rk[4] = rk[0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {
+ return 0;
+ }
+ rk += 4;
+ }
+ }
+ rk[4] = GETU32(userKey + 16);
+ rk[5] = GETU32(userKey + 20);
+ if (bits == 192) {
+ while (1) {
+ temp = rk[ 5];
+ rk[ 6] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 7] = rk[ 1] ^ rk[ 6];
+ rk[ 8] = rk[ 2] ^ rk[ 7];
+ rk[ 9] = rk[ 3] ^ rk[ 8];
+ if (++i == 8) {
+ return 0;
+ }
+ rk[10] = rk[ 4] ^ rk[ 9];
+ rk[11] = rk[ 5] ^ rk[10];
+ rk += 6;
+ }
+ }
+ rk[6] = GETU32(userKey + 24);
+ rk[7] = GETU32(userKey + 28);
+ if (bits == 256) {
+ while (1) {
+ temp = rk[ 7];
+ rk[ 8] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 9] = rk[ 1] ^ rk[ 8];
+ rk[10] = rk[ 2] ^ rk[ 9];
+ rk[11] = rk[ 3] ^ rk[10];
+ if (++i == 7) {
+ return 0;
+ }
+ temp = rk[11];
+ rk[12] = rk[ 4] ^
+ (Te4[(temp >> 24) ] & 0xff000000) ^
+ (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp ) & 0xff] & 0x000000ff);
+ rk[13] = rk[ 5] ^ rk[12];
+ rk[14] = rk[ 6] ^ rk[13];
+ rk[15] = rk[ 7] ^ rk[14];
+
+ rk += 8;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key) {
+
+ u32 *rk;
+ int i, j, status;
+ u32 temp;
+
+ /* first, start with an encryption schedule */
+ status = AES_set_encrypt_key(userKey, bits, key);
+ if (status < 0)
+ return status;
+
+ rk = key->rd_key;
+
+ /* invert the order of the round keys: */
+ for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+ temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp;
+ temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+ temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+ temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+ }
+ /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+ for (i = 1; i < (key->rounds); i++) {
+ rk += 4;
+ rk[0] =
+ Td0[Te4[(rk[0] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[0] ) & 0xff] & 0xff];
+ rk[1] =
+ Td0[Te4[(rk[1] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[1] ) & 0xff] & 0xff];
+ rk[2] =
+ Td0[Te4[(rk[2] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[2] ) & 0xff] & 0xff];
+ rk[3] =
+ Td0[Te4[(rk[3] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[3] ) & 0xff] & 0xff];
+ }
+ return 0;
+}
+
+#ifndef AES_ASM
+/*
+ * Encrypt a single block
+ * in and out can overlap
+ */
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key) {
+
+ const u32 *rk;
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ assert(in && out && key);
+ rk = key->rd_key;
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(in ) ^ rk[0];
+ s1 = GETU32(in + 4) ^ rk[1];
+ s2 = GETU32(in + 8) ^ rk[2];
+ s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+ if (key->rounds > 10) {
+ /* round 10: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+ if (key->rounds > 12) {
+ /* round 12: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+ }
+ }
+ rk += key->rounds << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = key->rounds >> 1;
+ for (;;) {
+ t0 =
+ Te0[(s0 >> 24) ] ^
+ Te1[(s1 >> 16) & 0xff] ^
+ Te2[(s2 >> 8) & 0xff] ^
+ Te3[(s3 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Te0[(s1 >> 24) ] ^
+ Te1[(s2 >> 16) & 0xff] ^
+ Te2[(s3 >> 8) & 0xff] ^
+ Te3[(s0 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Te0[(s2 >> 24) ] ^
+ Te1[(s3 >> 16) & 0xff] ^
+ Te2[(s0 >> 8) & 0xff] ^
+ Te3[(s1 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Te0[(s3 >> 24) ] ^
+ Te1[(s0 >> 16) & 0xff] ^
+ Te2[(s1 >> 8) & 0xff] ^
+ Te3[(s2 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Te0[(t0 >> 24) ] ^
+ Te1[(t1 >> 16) & 0xff] ^
+ Te2[(t2 >> 8) & 0xff] ^
+ Te3[(t3 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Te0[(t1 >> 24) ] ^
+ Te1[(t2 >> 16) & 0xff] ^
+ Te2[(t3 >> 8) & 0xff] ^
+ Te3[(t0 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Te0[(t2 >> 24) ] ^
+ Te1[(t3 >> 16) & 0xff] ^
+ Te2[(t0 >> 8) & 0xff] ^
+ Te3[(t1 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Te0[(t3 >> 24) ] ^
+ Te1[(t0 >> 16) & 0xff] ^
+ Te2[(t1 >> 8) & 0xff] ^
+ Te3[(t2 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Te4[(t0 >> 24) ] & 0xff000000) ^
+ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(out , s0);
+ s1 =
+ (Te4[(t1 >> 24) ] & 0xff000000) ^
+ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(out + 4, s1);
+ s2 =
+ (Te4[(t2 >> 24) ] & 0xff000000) ^
+ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(out + 8, s2);
+ s3 =
+ (Te4[(t3 >> 24) ] & 0xff000000) ^
+ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(out + 12, s3);
+}
+
+/*
+ * Decrypt a single block
+ * in and out can overlap
+ */
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key) {
+
+ const u32 *rk;
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ assert(in && out && key);
+ rk = key->rd_key;
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(in ) ^ rk[0];
+ s1 = GETU32(in + 4) ^ rk[1];
+ s2 = GETU32(in + 8) ^ rk[2];
+ s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+ if (key->rounds > 10) {
+ /* round 10: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+ if (key->rounds > 12) {
+ /* round 12: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+ }
+ }
+ rk += key->rounds << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = key->rounds >> 1;
+ for (;;) {
+ t0 =
+ Td0[(s0 >> 24) ] ^
+ Td1[(s3 >> 16) & 0xff] ^
+ Td2[(s2 >> 8) & 0xff] ^
+ Td3[(s1 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Td0[(s1 >> 24) ] ^
+ Td1[(s0 >> 16) & 0xff] ^
+ Td2[(s3 >> 8) & 0xff] ^
+ Td3[(s2 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Td0[(s2 >> 24) ] ^
+ Td1[(s1 >> 16) & 0xff] ^
+ Td2[(s0 >> 8) & 0xff] ^
+ Td3[(s3 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Td0[(s3 >> 24) ] ^
+ Td1[(s2 >> 16) & 0xff] ^
+ Td2[(s1 >> 8) & 0xff] ^
+ Td3[(s0 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Td0[(t0 >> 24) ] ^
+ Td1[(t3 >> 16) & 0xff] ^
+ Td2[(t2 >> 8) & 0xff] ^
+ Td3[(t1 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Td0[(t1 >> 24) ] ^
+ Td1[(t0 >> 16) & 0xff] ^
+ Td2[(t3 >> 8) & 0xff] ^
+ Td3[(t2 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Td0[(t2 >> 24) ] ^
+ Td1[(t1 >> 16) & 0xff] ^
+ Td2[(t0 >> 8) & 0xff] ^
+ Td3[(t3 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Td0[(t3 >> 24) ] ^
+ Td1[(t2 >> 16) & 0xff] ^
+ Td2[(t1 >> 8) & 0xff] ^
+ Td3[(t0 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Td4[(t0 >> 24) ] & 0xff000000) ^
+ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(out , s0);
+ s1 =
+ (Td4[(t1 >> 24) ] & 0xff000000) ^
+ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(out + 4, s1);
+ s2 =
+ (Td4[(t2 >> 24) ] & 0xff000000) ^
+ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(out + 8, s2);
+ s3 =
+ (Td4[(t3 >> 24) ] & 0xff000000) ^
+ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(out + 12, s3);
+}
+
+#endif /* AES_ASM */
+
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ const unsigned long length, const AES_KEY *key,
+ unsigned char *ivec, const int enc)
+{
+
+ unsigned long n;
+ unsigned long len = length;
+ unsigned char tmp[AES_BLOCK_SIZE];
+
+ assert(in && out && key && ivec);
+
+ if (enc) {
+ while (len >= AES_BLOCK_SIZE) {
+ for(n=0; n < AES_BLOCK_SIZE; ++n)
+ tmp[n] = in[n] ^ ivec[n];
+ AES_encrypt(tmp, out, key);
+ memcpy(ivec, out, AES_BLOCK_SIZE);
+ len -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+ if (len) {
+ for(n=0; n < len; ++n)
+ tmp[n] = in[n] ^ ivec[n];
+ for(n=len; n < AES_BLOCK_SIZE; ++n)
+ tmp[n] = ivec[n];
+ AES_encrypt(tmp, tmp, key);
+ memcpy(out, tmp, AES_BLOCK_SIZE);
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ }
+ } else {
+ while (len >= AES_BLOCK_SIZE) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(in, out, key);
+ for(n=0; n < AES_BLOCK_SIZE; ++n)
+ out[n] ^= ivec[n];
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ len -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+ if (len) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(tmp, tmp, key);
+ for(n=0; n < len; ++n)
+ out[n] = tmp[n] ^ ivec[n];
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ }
+ }
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/aes.h b/jslinux-2019-12-21/tinyemu-2019-12-21/aes.h
new file mode 100644
index 0000000..c4d7a1c
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/aes.h
@@ -0,0 +1,49 @@
+/*
+ * OpenSSL compatible AES header
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef AES_H
+#define AES_H
+
+#define AES_MAXNR 14
+#define AES_BLOCK_SIZE 16
+
+struct aes_key_st {
+ uint32_t rd_key[4 *(AES_MAXNR + 1)];
+ int rounds;
+};
+typedef struct aes_key_st AES_KEY;
+
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key);
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key);
+
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key);
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key);
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ const unsigned long length, const AES_KEY *key,
+ unsigned char *ivec, const int enc);
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/block_net.c b/jslinux-2019-12-21/tinyemu-2019-12-21/block_net.c
new file mode 100644
index 0000000..66794d4
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/block_net.c
@@ -0,0 +1,531 @@
+/*
+ * HTTP block device
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "cutils.h"
+#include "virtio.h"
+#include "fs_wget.h"
+#include "list.h"
+#include "fbuf.h"
+#include "machine.h"
+
+typedef enum {
+ CBLOCK_LOADING,
+ CBLOCK_LOADED,
+} CachedBlockStateEnum;
+
+typedef struct CachedBlock {
+ struct list_head link;
+ struct BlockDeviceHTTP *bf;
+ unsigned int block_num;
+ CachedBlockStateEnum state;
+ FileBuffer fbuf;
+} CachedBlock;
+
+#define BLK_FMT "%sblk%09u.bin"
+#define GROUP_FMT "%sgrp%09u.bin"
+#define PREFETCH_GROUP_LEN_MAX 32
+
+typedef struct {
+ struct BlockDeviceHTTP *bf;
+ int group_num;
+ int n_block_num;
+ CachedBlock *tab_block[PREFETCH_GROUP_LEN_MAX];
+} PrefetchGroupRequest;
+
+/* modified data is stored per cluster (smaller than cached blocks to
+ avoid losing space) */
+typedef struct Cluster {
+ FileBuffer fbuf;
+} Cluster;
+
+typedef struct BlockDeviceHTTP {
+ BlockDevice *bs;
+ int max_cache_size_kb;
+ char url[1024];
+ int prefetch_count;
+ void (*start_cb)(void *opaque);
+ void *start_opaque;
+
+ int64_t nb_sectors;
+ int block_size; /* in sectors, power of two */
+ int nb_blocks;
+ struct list_head cached_blocks; /* list of CachedBlock */
+ int n_cached_blocks;
+ int n_cached_blocks_max;
+
+ /* write support */
+ int sectors_per_cluster; /* power of two */
+ Cluster **clusters; /* NULL if no written data */
+ int n_clusters;
+ int n_allocated_clusters;
+
+ /* statistics */
+ int64_t n_read_sectors;
+ int64_t n_read_blocks;
+ int64_t n_write_sectors;
+
+ /* current read request */
+ BOOL is_write;
+ uint64_t sector_num;
+ int cur_block_num;
+ int sector_index, sector_count;
+ BlockDeviceCompletionFunc *cb;
+ void *opaque;
+ uint8_t *io_buf;
+
+ /* prefetch */
+ int prefetch_group_len;
+} BlockDeviceHTTP;
+
+static void bf_update_block(CachedBlock *b, const uint8_t *data);
+static void bf_read_onload(void *opaque, int err, void *data, size_t size);
+static void bf_init_onload(void *opaque, int err, void *data, size_t size);
+static void bf_prefetch_group_onload(void *opaque, int err, void *data,
+ size_t size);
+
+static CachedBlock *bf_find_block(BlockDeviceHTTP *bf, unsigned int block_num)
+{
+ CachedBlock *b;
+ struct list_head *el;
+
+ list_for_each(el, &bf->cached_blocks) {
+ b = list_entry(el, CachedBlock, link);
+ if (b->block_num == block_num) {
+ /* move to front */
+ if (bf->cached_blocks.next != el) {
+ list_del(&b->link);
+ list_add(&b->link, &bf->cached_blocks);
+ }
+ return b;
+ }
+ }
+ return NULL;
+}
+
+static void bf_free_block(BlockDeviceHTTP *bf, CachedBlock *b)
+{
+ bf->n_cached_blocks--;
+ file_buffer_reset(&b->fbuf);
+ list_del(&b->link);
+ free(b);
+}
+
+static CachedBlock *bf_add_block(BlockDeviceHTTP *bf, unsigned int block_num)
+{
+ CachedBlock *b;
+ if (bf->n_cached_blocks >= bf->n_cached_blocks_max) {
+ struct list_head *el, *el1;
+ /* start by looking at the least unused blocks */
+ list_for_each_prev_safe(el, el1, &bf->cached_blocks) {
+ b = list_entry(el, CachedBlock, link);
+ if (b->state == CBLOCK_LOADED) {
+ bf_free_block(bf, b);
+ if (bf->n_cached_blocks < bf->n_cached_blocks_max)
+ break;
+ }
+ }
+ }
+ b = mallocz(sizeof(CachedBlock));
+ b->bf = bf;
+ b->block_num = block_num;
+ b->state = CBLOCK_LOADING;
+ file_buffer_init(&b->fbuf);
+ file_buffer_resize(&b->fbuf, bf->block_size * 512);
+ list_add(&b->link, &bf->cached_blocks);
+ bf->n_cached_blocks++;
+ return b;
+}
+
+static int64_t bf_get_sector_count(BlockDevice *bs)
+{
+ BlockDeviceHTTP *bf = bs->opaque;
+ return bf->nb_sectors;
+}
+
+static void bf_start_load_block(BlockDevice *bs, int block_num)
+{
+ BlockDeviceHTTP *bf = bs->opaque;
+ char filename[1024];
+ CachedBlock *b;
+ b = bf_add_block(bf, block_num);
+ bf->n_read_blocks++;
+ /* make a XHR to read the block */
+#if 0
+ printf("%u,\n", block_num);
+#endif
+#if 0
+ printf("load_blk=%d cached=%d read=%d KB (%d KB) write=%d KB (%d KB)\n",
+ block_num, bf->n_cached_blocks,
+ (int)(bf->n_read_sectors / 2),
+ (int)(bf->n_read_blocks * bf->block_size / 2),
+ (int)(bf->n_write_sectors / 2),
+ (int)(bf->n_allocated_clusters * bf->sectors_per_cluster / 2));
+#endif
+ snprintf(filename, sizeof(filename), BLK_FMT, bf->url, block_num);
+ // printf("wget %s\n", filename);
+ fs_wget(filename, NULL, NULL, b, bf_read_onload, TRUE);
+}
+
+static void bf_start_load_prefetch_group(BlockDevice *bs, int group_num,
+ const int *tab_block_num,
+ int n_block_num)
+{
+ BlockDeviceHTTP *bf = bs->opaque;
+ CachedBlock *b;
+ PrefetchGroupRequest *req;
+ char filename[1024];
+ BOOL req_flag;
+ int i;
+
+ req_flag = FALSE;
+ req = malloc(sizeof(*req));
+ req->bf = bf;
+ req->group_num = group_num;
+ req->n_block_num = n_block_num;
+ for(i = 0; i < n_block_num; i++) {
+ b = bf_find_block(bf, tab_block_num[i]);
+ if (!b) {
+ b = bf_add_block(bf, tab_block_num[i]);
+ req_flag = TRUE;
+ } else {
+ /* no need to read the block if it is already loading or
+ loaded */
+ b = NULL;
+ }
+ req->tab_block[i] = b;
+ }
+
+ if (req_flag) {
+ snprintf(filename, sizeof(filename), GROUP_FMT, bf->url, group_num);
+ // printf("wget %s\n", filename);
+ fs_wget(filename, NULL, NULL, req, bf_prefetch_group_onload, TRUE);
+ /* XXX: should add request in a list to free it for clean exit */
+ } else {
+ free(req);
+ }
+}
+
+static void bf_prefetch_group_onload(void *opaque, int err, void *data,
+ size_t size)
+{
+ PrefetchGroupRequest *req = opaque;
+ BlockDeviceHTTP *bf = req->bf;
+ CachedBlock *b;
+ int block_bytes, i;
+
+ if (err < 0) {
+ fprintf(stderr, "Could not load group %u\n", req->group_num);
+ exit(1);
+ }
+ block_bytes = bf->block_size * 512;
+ assert(size == block_bytes * req->n_block_num);
+ for(i = 0; i < req->n_block_num; i++) {
+ b = req->tab_block[i];
+ if (b) {
+ bf_update_block(b, (const uint8_t *)data + block_bytes * i);
+ }
+ }
+ free(req);
+}
+
+static int bf_rw_async1(BlockDevice *bs, BOOL is_sync)
+{
+ BlockDeviceHTTP *bf = bs->opaque;
+ int offset, block_num, n, cluster_num;
+ CachedBlock *b;
+ Cluster *c;
+
+ for(;;) {
+ n = bf->sector_count - bf->sector_index;
+ if (n == 0)
+ break;
+ cluster_num = bf->sector_num / bf->sectors_per_cluster;
+ c = bf->clusters[cluster_num];
+ if (c) {
+ offset = bf->sector_num % bf->sectors_per_cluster;
+ n = min_int(n, bf->sectors_per_cluster - offset);
+ if (bf->is_write) {
+ file_buffer_write(&c->fbuf, offset * 512,
+ bf->io_buf + bf->sector_index * 512, n * 512);
+ } else {
+ file_buffer_read(&c->fbuf, offset * 512,
+ bf->io_buf + bf->sector_index * 512, n * 512);
+ }
+ bf->sector_index += n;
+ bf->sector_num += n;
+ } else {
+ block_num = bf->sector_num / bf->block_size;
+ offset = bf->sector_num % bf->block_size;
+ n = min_int(n, bf->block_size - offset);
+ bf->cur_block_num = block_num;
+
+ b = bf_find_block(bf, block_num);
+ if (b) {
+ if (b->state == CBLOCK_LOADING) {
+ /* wait until the block is loaded */
+ return 1;
+ } else {
+ if (bf->is_write) {
+ int cluster_size, cluster_offset;
+ uint8_t *buf;
+ /* allocate a new cluster */
+ c = mallocz(sizeof(Cluster));
+ cluster_size = bf->sectors_per_cluster * 512;
+ buf = malloc(cluster_size);
+ file_buffer_init(&c->fbuf);
+ file_buffer_resize(&c->fbuf, cluster_size);
+ bf->clusters[cluster_num] = c;
+ /* copy the cached block data to the cluster */
+ cluster_offset = (cluster_num * bf->sectors_per_cluster) &
+ (bf->block_size - 1);
+ file_buffer_read(&b->fbuf, cluster_offset * 512,
+ buf, cluster_size);
+ file_buffer_write(&c->fbuf, 0, buf, cluster_size);
+ free(buf);
+ bf->n_allocated_clusters++;
+ continue; /* write to the allocated cluster */
+ } else {
+ file_buffer_read(&b->fbuf, offset * 512,
+ bf->io_buf + bf->sector_index * 512, n * 512);
+ }
+ bf->sector_index += n;
+ bf->sector_num += n;
+ }
+ } else {
+ bf_start_load_block(bs, block_num);
+ return 1;
+ }
+ bf->cur_block_num = -1;
+ }
+ }
+
+ if (!is_sync) {
+ // printf("end of request\n");
+ /* end of request */
+ bf->cb(bf->opaque, 0);
+ }
+ return 0;
+}
+
+static void bf_update_block(CachedBlock *b, const uint8_t *data)
+{
+ BlockDeviceHTTP *bf = b->bf;
+ BlockDevice *bs = bf->bs;
+
+ assert(b->state == CBLOCK_LOADING);
+ file_buffer_write(&b->fbuf, 0, data, bf->block_size * 512);
+ b->state = CBLOCK_LOADED;
+
+ /* continue I/O read/write if necessary */
+ if (b->block_num == bf->cur_block_num) {
+ bf_rw_async1(bs, FALSE);
+ }
+}
+
+static void bf_read_onload(void *opaque, int err, void *data, size_t size)
+{
+ CachedBlock *b = opaque;
+ BlockDeviceHTTP *bf = b->bf;
+
+ if (err < 0) {
+ fprintf(stderr, "Could not load block %u\n", b->block_num);
+ exit(1);
+ }
+
+ assert(size == bf->block_size * 512);
+ bf_update_block(b, data);
+}
+
+static int bf_read_async(BlockDevice *bs,
+ uint64_t sector_num, uint8_t *buf, int n,
+ BlockDeviceCompletionFunc *cb, void *opaque)
+{
+ BlockDeviceHTTP *bf = bs->opaque;
+ // printf("bf_read_async: sector_num=%" PRId64 " n=%d\n", sector_num, n);
+ bf->is_write = FALSE;
+ bf->sector_num = sector_num;
+ bf->io_buf = buf;
+ bf->sector_count = n;
+ bf->sector_index = 0;
+ bf->cb = cb;
+ bf->opaque = opaque;
+ bf->n_read_sectors += n;
+ return bf_rw_async1(bs, TRUE);
+}
+
+static int bf_write_async(BlockDevice *bs,
+ uint64_t sector_num, const uint8_t *buf, int n,
+ BlockDeviceCompletionFunc *cb, void *opaque)
+{
+ BlockDeviceHTTP *bf = bs->opaque;
+ // printf("bf_write_async: sector_num=%" PRId64 " n=%d\n", sector_num, n);
+ bf->is_write = TRUE;
+ bf->sector_num = sector_num;
+ bf->io_buf = (uint8_t *)buf;
+ bf->sector_count = n;
+ bf->sector_index = 0;
+ bf->cb = cb;
+ bf->opaque = opaque;
+ bf->n_write_sectors += n;
+ return bf_rw_async1(bs, TRUE);
+}
+
+BlockDevice *block_device_init_http(const char *url,
+ int max_cache_size_kb,
+ void (*start_cb)(void *opaque),
+ void *start_opaque)
+{
+ BlockDevice *bs;
+ BlockDeviceHTTP *bf;
+ char *p;
+
+ bs = mallocz(sizeof(*bs));
+ bf = mallocz(sizeof(*bf));
+ strcpy(bf->url, url);
+ /* get the path with the trailing '/' */
+ p = strrchr(bf->url, '/');
+ if (!p)
+ p = bf->url;
+ else
+ p++;
+ *p = '\0';
+
+ init_list_head(&bf->cached_blocks);
+ bf->max_cache_size_kb = max_cache_size_kb;
+ bf->start_cb = start_cb;
+ bf->start_opaque = start_opaque;
+ bf->bs = bs;
+
+ bs->opaque = bf;
+ bs->get_sector_count = bf_get_sector_count;
+ bs->read_async = bf_read_async;
+ bs->write_async = bf_write_async;
+
+ fs_wget(url, NULL, NULL, bs, bf_init_onload, TRUE);
+ return bs;
+}
+
+static void bf_init_onload(void *opaque, int err, void *data, size_t size)
+{
+ BlockDevice *bs = opaque;
+ BlockDeviceHTTP *bf = bs->opaque;
+ int block_size_kb, block_num;
+ JSONValue cfg, array;
+
+ if (err < 0) {
+ fprintf(stderr, "Could not load block device file (err=%d)\n", -err);
+ exit(1);
+ }
+
+ /* parse the disk image info */
+ cfg = json_parse_value_len(data, size);
+ if (json_is_error(cfg)) {
+ vm_error("error: %s\n", json_get_error(cfg));
+ config_error:
+ json_free(cfg);
+ exit(1);
+ }
+
+ if (vm_get_int(cfg, "block_size", &block_size_kb) < 0)
+ goto config_error;
+ bf->block_size = block_size_kb * 2;
+ if (bf->block_size <= 0 ||
+ (bf->block_size & (bf->block_size - 1)) != 0) {
+ vm_error("invalid block_size\n");
+ goto config_error;
+ }
+ if (vm_get_int(cfg, "n_block", &bf->nb_blocks) < 0)
+ goto config_error;
+ if (bf->nb_blocks <= 0) {
+ vm_error("invalid n_block\n");
+ goto config_error;
+ }
+
+ bf->nb_sectors = bf->block_size * (uint64_t)bf->nb_blocks;
+ bf->n_cached_blocks = 0;
+ bf->n_cached_blocks_max = max_int(1, bf->max_cache_size_kb / block_size_kb);
+ bf->cur_block_num = -1; /* no request in progress */
+
+ bf->sectors_per_cluster = 8; /* 4 KB */
+ bf->n_clusters = (bf->nb_sectors + bf->sectors_per_cluster - 1) / bf->sectors_per_cluster;
+ bf->clusters = mallocz(sizeof(bf->clusters[0]) * bf->n_clusters);
+
+ if (vm_get_int_opt(cfg, "prefetch_group_len",
+ &bf->prefetch_group_len, 1) < 0)
+ goto config_error;
+ if (bf->prefetch_group_len > PREFETCH_GROUP_LEN_MAX) {
+ vm_error("prefetch_group_len is too large");
+ goto config_error;
+ }
+
+ array = json_object_get(cfg, "prefetch");
+ if (!json_is_undefined(array)) {
+ int idx, prefetch_len, l, i;
+ JSONValue el;
+ int tab_block_num[PREFETCH_GROUP_LEN_MAX];
+
+ if (array.type != JSON_ARRAY) {
+ vm_error("expecting an array\n");
+ goto config_error;
+ }
+ prefetch_len = array.u.array->len;
+ idx = 0;
+ while (idx < prefetch_len) {
+ l = min_int(prefetch_len - idx, bf->prefetch_group_len);
+ for(i = 0; i < l; i++) {
+ el = json_array_get(array, idx + i);
+ if (el.type != JSON_INT) {
+ vm_error("expecting an integer\n");
+ goto config_error;
+ }
+ tab_block_num[i] = el.u.int32;
+ }
+ if (l == 1) {
+ block_num = tab_block_num[0];
+ if (!bf_find_block(bf, block_num)) {
+ bf_start_load_block(bs, block_num);
+ }
+ } else {
+ bf_start_load_prefetch_group(bs, idx / bf->prefetch_group_len,
+ tab_block_num, l);
+ }
+ idx += l;
+ }
+ }
+ json_free(cfg);
+
+ if (bf->start_cb) {
+ bf->start_cb(bf->start_opaque);
+ }
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/build_filelist.c b/jslinux-2019-12-21/tinyemu-2019-12-21/build_filelist.c
new file mode 100644
index 0000000..db3c150
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/build_filelist.c
@@ -0,0 +1,311 @@
+/*
+ * File list builder for RISCVEMU network filesystem
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <sys/statfs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/sysmacros.h>
+
+#include "cutils.h"
+#include "fs_utils.h"
+
+void print_str(FILE *f, const char *str)
+{
+ const char *s;
+ int c;
+ s = str;
+ while (*s != '\0') {
+ if (*s <= ' ' || *s > '~')
+ goto use_quote;
+ s++;
+ }
+ fputs(str, f);
+ return;
+ use_quote:
+ s = str;
+ fputc('"', f);
+ while (*s != '\0') {
+ c = *(uint8_t *)s;
+ if (c < ' ' || c == 127) {
+ fprintf(f, "\\x%02x", c);
+ } else if (c == '\\' || c == '\"') {
+ fprintf(f, "\\%c", c);
+ } else {
+ fputc(c, f);
+ }
+ s++;
+ }
+ fputc('"', f);
+}
+
+#define COPY_BUF_LEN (1024 * 1024)
+
+static void copy_file(const char *src_filename, const char *dst_filename)
+{
+ uint8_t *buf;
+ FILE *fi, *fo;
+ int len;
+
+ buf = malloc(COPY_BUF_LEN);
+ fi = fopen(src_filename, "rb");
+ if (!fi) {
+ perror(src_filename);
+ exit(1);
+ }
+ fo = fopen(dst_filename, "wb");
+ if (!fo) {
+ perror(dst_filename);
+ exit(1);
+ }
+ for(;;) {
+ len = fread(buf, 1, COPY_BUF_LEN, fi);
+ if (len == 0)
+ break;
+ fwrite(buf, 1, len, fo);
+ }
+ fclose(fo);
+ fclose(fi);
+}
+
+typedef struct {
+ char *files_path;
+ uint64_t next_inode_num;
+ uint64_t fs_size;
+ uint64_t fs_max_size;
+ FILE *f;
+} ScanState;
+
+static void add_file_size(ScanState *s, uint64_t size)
+{
+ s->fs_size += block_align(size, FS_BLOCK_SIZE);
+ if (s->fs_size > s->fs_max_size) {
+ fprintf(stderr, "Filesystem Quota exceeded (%" PRId64 " bytes)\n", s->fs_max_size);
+ exit(1);
+ }
+}
+
+void scan_dir(ScanState *s, const char *path)
+{
+ FILE *f = s->f;
+ DIR *dirp;
+ struct dirent *de;
+ const char *name;
+ struct stat st;
+ char *path1;
+ uint32_t mode, v;
+
+ dirp = opendir(path);
+ if (!dirp) {
+ perror(path);
+ exit(1);
+ }
+ for(;;) {
+ de = readdir(dirp);
+ if (!de)
+ break;
+ name = de->d_name;
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+ path1 = compose_path(path, name);
+ if (lstat(path1, &st) < 0) {
+ perror(path1);
+ exit(1);
+ }
+
+ mode = st.st_mode & 0xffff;
+ fprintf(f, "%06o %u %u",
+ mode,
+ (int)st.st_uid,
+ (int)st.st_gid);
+ if (S_ISCHR(mode) || S_ISBLK(mode)) {
+ fprintf(f, " %u %u",
+ (int)major(st.st_rdev),
+ (int)minor(st.st_rdev));
+ }
+ if (S_ISREG(mode)) {
+ fprintf(f, " %" PRIu64, st.st_size);
+ }
+ /* modification time (at most ms resolution) */
+ fprintf(f, " %u", (int)st.st_mtim.tv_sec);
+ v = st.st_mtim.tv_nsec;
+ if (v != 0) {
+ fprintf(f, ".");
+ while (v != 0) {
+ fprintf(f, "%u", v / 100000000);
+ v = (v % 100000000) * 10;
+ }
+ }
+
+ fprintf(f, " ");
+ print_str(f, name);
+ if (S_ISLNK(mode)) {
+ char buf[1024];
+ int len;
+ len = readlink(path1, buf, sizeof(buf) - 1);
+ if (len < 0) {
+ perror("readlink");
+ exit(1);
+ }
+ buf[len] = '\0';
+ fprintf(f, " ");
+ print_str(f, buf);
+ } else if (S_ISREG(mode) && st.st_size > 0) {
+ char buf1[FILEID_SIZE_MAX], *fname;
+ FSFileID file_id;
+ file_id = s->next_inode_num++;
+ fprintf(f, " %" PRIx64, file_id);
+ file_id_to_filename(buf1, file_id);
+ fname = compose_path(s->files_path, buf1);
+ copy_file(path1, fname);
+ add_file_size(s, st.st_size);
+ }
+
+ fprintf(f, "\n");
+ if (S_ISDIR(mode)) {
+ scan_dir(s, path1);
+ }
+ free(path1);
+ }
+
+ closedir(dirp);
+ fprintf(f, ".\n"); /* end of directory */
+}
+
+void help(void)
+{
+ printf("usage: build_filelist [options] source_path dest_path\n"
+ "\n"
+ "Options:\n"
+ "-m size_mb set the max filesystem size in MiB\n");
+ exit(1);
+}
+
+#define LOCK_FILENAME "lock"
+
+int main(int argc, char **argv)
+{
+ const char *dst_path, *src_path;
+ ScanState s_s, *s = &s_s;
+ FILE *f;
+ char *filename;
+ FSFileID root_id;
+ char fname[FILEID_SIZE_MAX];
+ struct stat st;
+ uint64_t first_inode, fs_max_size;
+ int c;
+
+ first_inode = 1;
+ fs_max_size = (uint64_t)1 << 30;
+ for(;;) {
+ c = getopt(argc, argv, "hi:m:");
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'h':
+ help();
+ case 'i':
+ first_inode = strtoul(optarg, NULL, 0);
+ break;
+ case 'm':
+ fs_max_size = (uint64_t)strtoul(optarg, NULL, 0) << 20;
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ if (optind + 1 >= argc)
+ help();
+ src_path = argv[optind];
+ dst_path = argv[optind + 1];
+
+ mkdir(dst_path, 0755);
+
+ s->files_path = compose_path(dst_path, ROOT_FILENAME);
+ s->next_inode_num = first_inode;
+ s->fs_size = 0;
+ s->fs_max_size = fs_max_size;
+
+ mkdir(s->files_path, 0755);
+
+ root_id = s->next_inode_num++;
+ file_id_to_filename(fname, root_id);
+ filename = compose_path(s->files_path, fname);
+ f = fopen(filename, "wb");
+ if (!f) {
+ perror(filename);
+ exit(1);
+ }
+ fprintf(f, "Version: 1\n");
+ fprintf(f, "Revision: 1\n");
+ fprintf(f, "\n");
+ s->f = f;
+ scan_dir(s, src_path);
+ fclose(f);
+
+ /* take into account the filelist size */
+ if (stat(filename, &st) < 0) {
+ perror(filename);
+ exit(1);
+ }
+ add_file_size(s, st.st_size);
+
+ free(filename);
+
+ filename = compose_path(dst_path, HEAD_FILENAME);
+ f = fopen(filename, "wb");
+ if (!f) {
+ perror(filename);
+ exit(1);
+ }
+ fprintf(f, "Version: 1\n");
+ fprintf(f, "Revision: 1\n");
+ fprintf(f, "NextFileID: %" PRIx64 "\n", s->next_inode_num);
+ fprintf(f, "FSFileCount: %" PRIu64 "\n", s->next_inode_num - 1);
+ fprintf(f, "FSSize: %" PRIu64 "\n", s->fs_size);
+ fprintf(f, "FSMaxSize: %" PRIu64 "\n", s->fs_max_size);
+ fprintf(f, "Key:\n"); /* not encrypted */
+ fprintf(f, "RootID: %" PRIx64 "\n", root_id);
+ fclose(f);
+ free(filename);
+
+ filename = compose_path(dst_path, LOCK_FILENAME);
+ f = fopen(filename, "wb");
+ if (!f) {
+ perror(filename);
+ exit(1);
+ }
+ fclose(f);
+ free(filename);
+
+ return 0;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/cutils.c b/jslinux-2019-12-21/tinyemu-2019-12-21/cutils.c
new file mode 100644
index 0000000..a86f90b
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/cutils.c
@@ -0,0 +1,120 @@
+/*
+ * Misc C utilities
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <ctype.h>
+
+#include "cutils.h"
+
+void *mallocz(size_t size)
+{
+ void *ptr;
+ ptr = malloc(size);
+ if (!ptr)
+ return NULL;
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+int strstart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+void dbuf_init(DynBuf *s)
+{
+ memset(s, 0, sizeof(*s));
+}
+
+void dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
+{
+ size_t end, new_size;
+ new_size = end = offset + len;
+ if (new_size > s->allocated_size) {
+ new_size = max_int(new_size, s->allocated_size * 3 / 2);
+ s->buf = realloc(s->buf, new_size);
+ s->allocated_size = new_size;
+ }
+ memcpy(s->buf + offset, data, len);
+ if (end > s->size)
+ s->size = end;
+}
+
+void dbuf_putc(DynBuf *s, uint8_t c)
+{
+ dbuf_write(s, s->size, &c, 1);
+}
+
+void dbuf_putstr(DynBuf *s, const char *str)
+{
+ dbuf_write(s, s->size, (const uint8_t *)str, strlen(str));
+}
+
+void dbuf_free(DynBuf *s)
+{
+ free(s->buf);
+ memset(s, 0, sizeof(*s));
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/cutils.h b/jslinux-2019-12-21/tinyemu-2019-12-21/cutils.h
new file mode 100644
index 0000000..689542e
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/cutils.h
@@ -0,0 +1,194 @@
+/*
+ * C utilities
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CUTILS_H
+#define CUTILS_H
+
+#include <inttypes.h>
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define force_inline inline __attribute__((always_inline))
+#define no_inline __attribute__((noinline))
+#define __maybe_unused __attribute__((unused))
+
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+#define countof(x) (sizeof(x) / sizeof(x[0]))
+
+#define DLL_PUBLIC __attribute__ ((visibility ("default")))
+
+#ifndef _BOOL_defined
+#define _BOOL_defined
+#undef FALSE
+#undef TRUE
+
+typedef int BOOL;
+enum {
+ FALSE = 0,
+ TRUE = 1,
+};
+#endif
+
+/* this test works at least with gcc */
+#if defined(__SIZEOF_INT128__)
+#define HAVE_INT128
+#endif
+
+#ifdef HAVE_INT128
+typedef __int128 int128_t;
+typedef unsigned __int128 uint128_t;
+#endif
+
+static inline int max_int(int a, int b)
+{
+ if (a > b)
+ return a;
+ else
+ return b;
+}
+
+static inline int min_int(int a, int b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+
+void *mallocz(size_t size);
+
+#if defined(_WIN32)
+static inline uint32_t bswap_32(uint32_t v)
+{
+ return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
+ ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
+}
+#else
+#include <byteswap.h>
+#endif
+
+static inline uint16_t get_le16(const uint8_t *ptr)
+{
+ return ptr[0] | (ptr[1] << 8);
+}
+
+static inline uint32_t get_le32(const uint8_t *ptr)
+{
+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+static inline uint64_t get_le64(const uint8_t *ptr)
+{
+ return get_le32(ptr) | ((uint64_t)get_le32(ptr + 4) << 32);
+}
+
+static inline void put_le16(uint8_t *ptr, uint16_t v)
+{
+ ptr[0] = v;
+ ptr[1] = v >> 8;
+}
+
+static inline void put_le32(uint8_t *ptr, uint32_t v)
+{
+ ptr[0] = v;
+ ptr[1] = v >> 8;
+ ptr[2] = v >> 16;
+ ptr[3] = v >> 24;
+}
+
+static inline void put_le64(uint8_t *ptr, uint64_t v)
+{
+ put_le32(ptr, v);
+ put_le32(ptr + 4, v >> 32);
+}
+
+static inline uint32_t get_be32(const uint8_t *d)
+{
+ return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void put_be32(uint8_t *d, uint32_t v)
+{
+ d[0] = v >> 24;
+ d[1] = v >> 16;
+ d[2] = v >> 8;
+ d[3] = v >> 0;
+}
+
+static inline void put_be64(uint8_t *d, uint64_t v)
+{
+ put_be32(d, v >> 32);
+ put_be32(d + 4, v);
+}
+
+#ifdef WORDS_BIGENDIAN
+static inline uint32_t cpu_to_be32(uint32_t v)
+{
+ return v;
+}
+#else
+static inline uint32_t cpu_to_be32(uint32_t v)
+{
+ return bswap_32(v);
+}
+#endif
+
+/* XXX: optimize */
+static inline int ctz32(uint32_t a)
+{
+ int i;
+ if (a == 0)
+ return 32;
+ for(i = 0; i < 32; i++) {
+ if ((a >> i) & 1)
+ return i;
+ }
+ return 32;
+}
+
+
+void *mallocz(size_t size);
+void pstrcpy(char *buf, int buf_size, const char *str);
+char *pstrcat(char *buf, int buf_size, const char *s);
+int strstart(const char *str, const char *val, const char **ptr);
+
+typedef struct {
+ uint8_t *buf;
+ size_t size;
+ size_t allocated_size;
+} DynBuf;
+
+void dbuf_init(DynBuf *s);
+void dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
+void dbuf_putc(DynBuf *s, uint8_t c);
+void dbuf_putstr(DynBuf *s, const char *str);
+void dbuf_free(DynBuf *s);
+
+#endif /* CUTILS_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fbuf.h b/jslinux-2019-12-21/tinyemu-2019-12-21/fbuf.h
new file mode 100644
index 0000000..9736a49
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fbuf.h
@@ -0,0 +1,22 @@
+#ifndef FBUF_H
+#define FBUF_H
+
+typedef struct {
+#if defined(EMSCRIPTEN)
+ int handle;
+#else
+ uint8_t *data;
+#endif
+ size_t allocated_size;
+} FileBuffer;
+
+void file_buffer_init(FileBuffer *bs);
+void file_buffer_reset(FileBuffer *bs);
+int file_buffer_resize(FileBuffer *bs, size_t new_size);
+void file_buffer_write(FileBuffer *bs, size_t offset, const uint8_t *buf,
+ size_t size);
+void file_buffer_set(FileBuffer *bs, size_t offset, int val, size_t size);
+void file_buffer_read(FileBuffer *bs, size_t offset, uint8_t *buf,
+ size_t size);
+
+#endif /* FBUF_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs.c b/jslinux-2019-12-21/tinyemu-2019-12-21/fs.c
new file mode 100644
index 0000000..7f92d82
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs.c
@@ -0,0 +1,104 @@
+/*
+ * Filesystem utilities
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "cutils.h"
+#include "fs.h"
+
+FSFile *fs_dup(FSDevice *fs, FSFile *f)
+{
+ FSQID qid;
+ fs->fs_walk(fs, &f, &qid, f, 0, NULL);
+ return f;
+}
+
+FSFile *fs_walk_path1(FSDevice *fs, FSFile *f, const char *path,
+ char **pname)
+{
+ const char *p;
+ char *name;
+ FSFile *f1;
+ FSQID qid;
+ int len, ret;
+ BOOL is_last, is_first;
+
+ if (path[0] == '/')
+ path++;
+
+ is_first = TRUE;
+ for(;;) {
+ p = strchr(path, '/');
+ if (!p) {
+ name = (char *)path;
+ if (pname) {
+ *pname = name;
+ if (is_first) {
+ ret = fs->fs_walk(fs, &f, &qid, f, 0, NULL);
+ if (ret < 0)
+ f = NULL;
+ }
+ return f;
+ }
+ is_last = TRUE;
+ } else {
+ len = p - path;
+ name = malloc(len + 1);
+ memcpy(name, path, len);
+ name[len] = '\0';
+ is_last = FALSE;
+ }
+ ret = fs->fs_walk(fs, &f1, &qid, f, 1, &name);
+ if (!is_last)
+ free(name);
+ if (!is_first)
+ fs->fs_delete(fs, f);
+ f = f1;
+ is_first = FALSE;
+ if (ret <= 0) {
+ fs->fs_delete(fs, f);
+ f = NULL;
+ break;
+ } else if (is_last) {
+ break;
+ }
+ path = p + 1;
+ }
+ return f;
+}
+
+FSFile *fs_walk_path(FSDevice *fs, FSFile *f, const char *path)
+{
+ return fs_walk_path1(fs, f, path, NULL);
+}
+
+void fs_end(FSDevice *fs)
+{
+ fs->fs_end(fs);
+ free(fs);
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs.h b/jslinux-2019-12-21/tinyemu-2019-12-21/fs.h
new file mode 100644
index 0000000..4c38e84
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs.h
@@ -0,0 +1,211 @@
+/*
+ * Filesystem abstraction
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* FSQID.type */
+#define P9_QTDIR 0x80
+#define P9_QTAPPEND 0x40
+#define P9_QTEXCL 0x20
+#define P9_QTMOUNT 0x10
+#define P9_QTAUTH 0x08
+#define P9_QTTMP 0x04
+#define P9_QTSYMLINK 0x02
+#define P9_QTLINK 0x01
+#define P9_QTFILE 0x00
+
+/* mode bits */
+#define P9_S_IRWXUGO 0x01FF
+#define P9_S_ISVTX 0x0200
+#define P9_S_ISGID 0x0400
+#define P9_S_ISUID 0x0800
+
+#define P9_S_IFMT 0xF000
+#define P9_S_IFIFO 0x1000
+#define P9_S_IFCHR 0x2000
+#define P9_S_IFDIR 0x4000
+#define P9_S_IFBLK 0x6000
+#define P9_S_IFREG 0x8000
+#define P9_S_IFLNK 0xA000
+#define P9_S_IFSOCK 0xC000
+
+/* flags for lopen()/lcreate() */
+#define P9_O_RDONLY 0x00000000
+#define P9_O_WRONLY 0x00000001
+#define P9_O_RDWR 0x00000002
+#define P9_O_NOACCESS 0x00000003
+#define P9_O_CREAT 0x00000040
+#define P9_O_EXCL 0x00000080
+#define P9_O_NOCTTY 0x00000100
+#define P9_O_TRUNC 0x00000200
+#define P9_O_APPEND 0x00000400
+#define P9_O_NONBLOCK 0x00000800
+#define P9_O_DSYNC 0x00001000
+#define P9_O_FASYNC 0x00002000
+#define P9_O_DIRECT 0x00004000
+#define P9_O_LARGEFILE 0x00008000
+#define P9_O_DIRECTORY 0x00010000
+#define P9_O_NOFOLLOW 0x00020000
+#define P9_O_NOATIME 0x00040000
+#define P9_O_CLOEXEC 0x00080000
+#define P9_O_SYNC 0x00100000
+
+/* for fs_setattr() */
+#define P9_SETATTR_MODE 0x00000001
+#define P9_SETATTR_UID 0x00000002
+#define P9_SETATTR_GID 0x00000004
+#define P9_SETATTR_SIZE 0x00000008
+#define P9_SETATTR_ATIME 0x00000010
+#define P9_SETATTR_MTIME 0x00000020
+#define P9_SETATTR_CTIME 0x00000040
+#define P9_SETATTR_ATIME_SET 0x00000080
+#define P9_SETATTR_MTIME_SET 0x00000100
+
+#define P9_EPERM 1
+#define P9_ENOENT 2
+#define P9_EIO 5
+#define P9_EEXIST 17
+#define P9_ENOTDIR 20
+#define P9_EINVAL 22
+#define P9_ENOSPC 28
+#define P9_ENOTEMPTY 39
+#define P9_EPROTO 71
+#define P9_ENOTSUP 524
+
+typedef struct FSDevice FSDevice;
+typedef struct FSFile FSFile;
+
+typedef struct {
+ uint32_t f_bsize;
+ uint64_t f_blocks;
+ uint64_t f_bfree;
+ uint64_t f_bavail;
+ uint64_t f_files;
+ uint64_t f_ffree;
+} FSStatFS;
+
+typedef struct {
+ uint8_t type; /* P9_IFx */
+ uint32_t version;
+ uint64_t path;
+} FSQID;
+
+typedef struct {
+ FSQID qid;
+ uint32_t st_mode;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint64_t st_nlink;
+ uint64_t st_rdev;
+ uint64_t st_size;
+ uint64_t st_blksize;
+ uint64_t st_blocks;
+ uint64_t st_atime_sec;
+ uint32_t st_atime_nsec;
+ uint64_t st_mtime_sec;
+ uint32_t st_mtime_nsec;
+ uint64_t st_ctime_sec;
+ uint32_t st_ctime_nsec;
+} FSStat;
+
+#define P9_LOCK_TYPE_RDLCK 0
+#define P9_LOCK_TYPE_WRLCK 1
+#define P9_LOCK_TYPE_UNLCK 2
+
+#define P9_LOCK_FLAGS_BLOCK 1
+#define P9_LOCK_FLAGS_RECLAIM 2
+
+#define P9_LOCK_SUCCESS 0
+#define P9_LOCK_BLOCKED 1
+#define P9_LOCK_ERROR 2
+#define P9_LOCK_GRACE 3
+
+#define FSCMD_NAME ".fscmd"
+
+typedef struct {
+ uint8_t type;
+ uint32_t flags;
+ uint64_t start;
+ uint64_t length;
+ uint32_t proc_id;
+ char *client_id;
+} FSLock;
+
+typedef void FSOpenCompletionFunc(FSDevice *fs, FSQID *qid, int err,
+ void *opaque);
+
+struct FSDevice {
+ void (*fs_end)(FSDevice *s);
+ void (*fs_delete)(FSDevice *s, FSFile *f);
+ void (*fs_statfs)(FSDevice *fs, FSStatFS *st);
+ int (*fs_attach)(FSDevice *fs, FSFile **pf, FSQID *qid, uint32_t uid,
+ const char *uname, const char *aname);
+ int (*fs_walk)(FSDevice *fs, FSFile **pf, FSQID *qids,
+ FSFile *f, int n, char **names);
+ int (*fs_mkdir)(FSDevice *fs, FSQID *qid, FSFile *f,
+ const char *name, uint32_t mode, uint32_t gid);
+ int (*fs_open)(FSDevice *fs, FSQID *qid, FSFile *f, uint32_t flags,
+ FSOpenCompletionFunc *cb, void *opaque);
+ int (*fs_create)(FSDevice *fs, FSQID *qid, FSFile *f, const char *name,
+ uint32_t flags, uint32_t mode, uint32_t gid);
+ int (*fs_stat)(FSDevice *fs, FSFile *f, FSStat *st);
+ int (*fs_setattr)(FSDevice *fs, FSFile *f, uint32_t mask,
+ uint32_t mode, uint32_t uid, uint32_t gid,
+ uint64_t size, uint64_t atime_sec, uint64_t atime_nsec,
+ uint64_t mtime_sec, uint64_t mtime_nsec);
+ void (*fs_close)(FSDevice *fs, FSFile *f);
+ int (*fs_readdir)(FSDevice *fs, FSFile *f, uint64_t offset,
+ uint8_t *buf, int count);
+ int (*fs_read)(FSDevice *fs, FSFile *f, uint64_t offset,
+ uint8_t *buf, int count);
+ int (*fs_write)(FSDevice *fs, FSFile *f, uint64_t offset,
+ const uint8_t *buf, int count);
+ int (*fs_link)(FSDevice *fs, FSFile *df, FSFile *f, const char *name);
+ int (*fs_symlink)(FSDevice *fs, FSQID *qid,
+ FSFile *f, const char *name, const char *symgt, uint32_t gid);
+ int (*fs_mknod)(FSDevice *fs, FSQID *qid,
+ FSFile *f, const char *name, uint32_t mode, uint32_t major,
+ uint32_t minor, uint32_t gid);
+ int (*fs_readlink)(FSDevice *fs, char *buf, int buf_size, FSFile *f);
+ int (*fs_renameat)(FSDevice *fs, FSFile *f, const char *name,
+ FSFile *new_f, const char *new_name);
+ int (*fs_unlinkat)(FSDevice *fs, FSFile *f, const char *name);
+ int (*fs_lock)(FSDevice *fs, FSFile *f, const FSLock *lock);
+ int (*fs_getlock)(FSDevice *fs, FSFile *f, FSLock *lock);
+};
+
+FSDevice *fs_disk_init(const char *root_path);
+FSDevice *fs_mem_init(void);
+FSDevice *fs_net_init(const char *url, void (*start)(void *opaque), void *opaque);
+void fs_net_set_pwd(FSDevice *fs, const char *pwd);
+#ifdef EMSCRIPTEN
+void fs_import_file(const char *filename, uint8_t *buf, int buf_len);
+#endif
+void fs_export_file(const char *filename,
+ const uint8_t *buf, int buf_len);
+void fs_end(FSDevice *fs);
+void fs_dump_cache_load(FSDevice *fs1, const char *filename);
+
+FSFile *fs_dup(FSDevice *fs, FSFile *f);
+FSFile *fs_walk_path1(FSDevice *fs, FSFile *f, const char *path,
+ char **pname);
+FSFile *fs_walk_path(FSDevice *fs, FSFile *f, const char *path);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs_disk.c b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_disk.c
new file mode 100644
index 0000000..bf96c89
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_disk.c
@@ -0,0 +1,659 @@
+/*
+ * Filesystem on disk
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <sys/statfs.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "cutils.h"
+#include "list.h"
+#include "fs.h"
+
+typedef struct {
+ FSDevice common;
+ char *root_path;
+} FSDeviceDisk;
+
+static void fs_close(FSDevice *fs, FSFile *f);
+
+struct FSFile {
+ uint32_t uid;
+ char *path; /* complete path */
+ BOOL is_opened;
+ BOOL is_dir;
+ union {
+ int fd;
+ DIR *dirp;
+ } u;
+};
+
+static void fs_delete(FSDevice *fs, FSFile *f)
+{
+ if (f->is_opened)
+ fs_close(fs, f);
+ free(f->path);
+ free(f);
+}
+
+/* warning: path belong to fid_create() */
+static FSFile *fid_create(FSDevice *s1, char *path, uint32_t uid)
+{
+ FSFile *f;
+ f = mallocz(sizeof(*f));
+ f->path = path;
+ f->uid = uid;
+ return f;
+}
+
+
+static int errno_table[][2] = {
+ { P9_EPERM, EPERM },
+ { P9_ENOENT, ENOENT },
+ { P9_EIO, EIO },
+ { P9_EEXIST, EEXIST },
+ { P9_EINVAL, EINVAL },
+ { P9_ENOSPC, ENOSPC },
+ { P9_ENOTEMPTY, ENOTEMPTY },
+ { P9_EPROTO, EPROTO },
+ { P9_ENOTSUP, ENOTSUP },
+};
+
+static int errno_to_p9(int err)
+{
+ int i;
+ if (err == 0)
+ return 0;
+ for(i = 0; i < countof(errno_table); i++) {
+ if (err == errno_table[i][1])
+ return errno_table[i][0];
+ }
+ return P9_EINVAL;
+}
+
+static int open_flags[][2] = {
+ { P9_O_CREAT, O_CREAT },
+ { P9_O_EXCL, O_EXCL },
+ // { P9_O_NOCTTY, O_NOCTTY },
+ { P9_O_TRUNC, O_TRUNC },
+ { P9_O_APPEND, O_APPEND },
+ { P9_O_NONBLOCK, O_NONBLOCK },
+ { P9_O_DSYNC, O_DSYNC },
+ // { P9_O_FASYNC, O_FASYNC },
+ // { P9_O_DIRECT, O_DIRECT },
+ // { P9_O_LARGEFILE, O_LARGEFILE },
+ // { P9_O_DIRECTORY, O_DIRECTORY },
+ { P9_O_NOFOLLOW, O_NOFOLLOW },
+ // { P9_O_NOATIME, O_NOATIME },
+ // { P9_O_CLOEXEC, O_CLOEXEC },
+ { P9_O_SYNC, O_SYNC },
+};
+
+static int p9_flags_to_host(int flags)
+{
+ int ret, i;
+
+ ret = (flags & P9_O_NOACCESS);
+ for(i = 0; i < countof(open_flags); i++) {
+ if (flags & open_flags[i][0])
+ ret |= open_flags[i][1];
+ }
+ return ret;
+}
+
+static void stat_to_qid(FSQID *qid, const struct stat *st)
+{
+ if (S_ISDIR(st->st_mode))
+ qid->type = P9_QTDIR;
+ else if (S_ISLNK(st->st_mode))
+ qid->type = P9_QTSYMLINK;
+ else
+ qid->type = P9_QTFILE;
+ qid->version = 0; /* no caching on client */
+ qid->path = st->st_ino;
+}
+
+static void fs_statfs(FSDevice *fs1, FSStatFS *st)
+{
+ FSDeviceDisk *fs = (FSDeviceDisk *)fs1;
+ struct statfs st1;
+ statfs(fs->root_path, &st1);
+ st->f_bsize = st1.f_bsize;
+ st->f_blocks = st1.f_blocks;
+ st->f_bfree = st1.f_bfree;
+ st->f_bavail = st1.f_bavail;
+ st->f_files = st1.f_files;
+ st->f_ffree = st1.f_ffree;
+}
+
+static char *compose_path(const char *path, const char *name)
+{
+ int path_len, name_len;
+ char *d;
+
+ path_len = strlen(path);
+ name_len = strlen(name);
+ d = malloc(path_len + 1 + name_len + 1);
+ memcpy(d, path, path_len);
+ d[path_len] = '/';
+ memcpy(d + path_len + 1, name, name_len + 1);
+ return d;
+}
+
+static int fs_attach(FSDevice *fs1, FSFile **pf,
+ FSQID *qid, uint32_t uid,
+ const char *uname, const char *aname)
+{
+ FSDeviceDisk *fs = (FSDeviceDisk *)fs1;
+ struct stat st;
+ FSFile *f;
+
+ if (lstat(fs->root_path, &st) != 0) {
+ *pf = NULL;
+ return -errno_to_p9(errno);
+ }
+ f = fid_create(fs1, strdup(fs->root_path), uid);
+ stat_to_qid(qid, &st);
+ *pf = f;
+ return 0;
+}
+
+static int fs_walk(FSDevice *fs, FSFile **pf, FSQID *qids,
+ FSFile *f, int n, char **names)
+{
+ char *path, *path1;
+ struct stat st;
+ int i;
+
+ path = strdup(f->path);
+ for(i = 0; i < n; i++) {
+ path1 = compose_path(path, names[i]);
+ if (lstat(path1, &st) != 0) {
+ free(path1);
+ break;
+ }
+ free(path);
+ path = path1;
+ stat_to_qid(&qids[i], &st);
+ }
+ *pf = fid_create(fs, path, f->uid);
+ return i;
+}
+
+
+static int fs_mkdir(FSDevice *fs, FSQID *qid, FSFile *f,
+ const char *name, uint32_t mode, uint32_t gid)
+{
+ char *path;
+ struct stat st;
+
+ path = compose_path(f->path, name);
+ if (mkdir(path, mode) < 0) {
+ free(path);
+ return -errno_to_p9(errno);
+ }
+ if (lstat(path, &st) != 0) {
+ free(path);
+ return -errno_to_p9(errno);
+ }
+ free(path);
+ stat_to_qid(qid, &st);
+ return 0;
+}
+
+static int fs_open(FSDevice *fs, FSQID *qid, FSFile *f, uint32_t flags,
+ FSOpenCompletionFunc *cb, void *opaque)
+{
+ struct stat st;
+ fs_close(fs, f);
+
+ if (stat(f->path, &st) != 0)
+ return -errno_to_p9(errno);
+ stat_to_qid(qid, &st);
+
+ if (flags & P9_O_DIRECTORY) {
+ DIR *dirp;
+ dirp = opendir(f->path);
+ if (!dirp)
+ return -errno_to_p9(errno);
+ f->is_opened = TRUE;
+ f->is_dir = TRUE;
+ f->u.dirp = dirp;
+ } else {
+ int fd;
+ fd = open(f->path, p9_flags_to_host(flags) & ~O_CREAT);
+ if (fd < 0)
+ return -errno_to_p9(errno);
+ f->is_opened = TRUE;
+ f->is_dir = FALSE;
+ f->u.fd = fd;
+ }
+ return 0;
+}
+
+static int fs_create(FSDevice *fs, FSQID *qid, FSFile *f, const char *name,
+ uint32_t flags, uint32_t mode, uint32_t gid)
+{
+ struct stat st;
+ char *path;
+ int ret, fd;
+
+ fs_close(fs, f);
+
+ path = compose_path(f->path, name);
+ fd = open(path, p9_flags_to_host(flags) | O_CREAT, mode);
+ if (fd < 0) {
+ free(path);
+ return -errno_to_p9(errno);
+ }
+ ret = lstat(path, &st);
+ if (ret != 0) {
+ free(path);
+ close(fd);
+ return -errno_to_p9(errno);
+ }
+ free(f->path);
+ f->path = path;
+ f->is_opened = TRUE;
+ f->is_dir = FALSE;
+ f->u.fd = fd;
+ stat_to_qid(qid, &st);
+ return 0;
+}
+
+static int fs_readdir(FSDevice *fs, FSFile *f, uint64_t offset,
+ uint8_t *buf, int count)
+{
+ struct dirent *de;
+ int len, pos, name_len, type, d_type;
+
+ if (!f->is_opened || !f->is_dir)
+ return -P9_EPROTO;
+ if (offset == 0)
+ rewinddir(f->u.dirp);
+ else
+ seekdir(f->u.dirp, offset);
+ pos = 0;
+ for(;;) {
+ de = readdir(f->u.dirp);
+ if (de == NULL)
+ break;
+ name_len = strlen(de->d_name);
+ len = 13 + 8 + 1 + 2 + name_len;
+ if ((pos + len) > count)
+ break;
+ offset = telldir(f->u.dirp);
+ d_type = de->d_type;
+ if (d_type == DT_UNKNOWN) {
+ char *path;
+ struct stat st;
+ path = compose_path(f->path, de->d_name);
+ if (lstat(path, &st) == 0) {
+ d_type = st.st_mode >> 12;
+ } else {
+ d_type = DT_REG; /* default */
+ }
+ free(path);
+ }
+ if (d_type == DT_DIR)
+ type = P9_QTDIR;
+ else if (d_type == DT_LNK)
+ type = P9_QTSYMLINK;
+ else
+ type = P9_QTFILE;
+ buf[pos++] = type;
+ put_le32(buf + pos, 0); /* version */
+ pos += 4;
+ put_le64(buf + pos, de->d_ino);
+ pos += 8;
+ put_le64(buf + pos, offset);
+ pos += 8;
+ buf[pos++] = d_type;
+ put_le16(buf + pos, name_len);
+ pos += 2;
+ memcpy(buf + pos, de->d_name, name_len);
+ pos += name_len;
+ }
+ return pos;
+}
+
+static int fs_read(FSDevice *fs, FSFile *f, uint64_t offset,
+ uint8_t *buf, int count)
+{
+ int ret;
+
+ if (!f->is_opened || f->is_dir)
+ return -P9_EPROTO;
+ ret = pread(f->u.fd, buf, count, offset);
+ if (ret < 0)
+ return -errno_to_p9(errno);
+ else
+ return ret;
+}
+
+static int fs_write(FSDevice *fs, FSFile *f, uint64_t offset,
+ const uint8_t *buf, int count)
+{
+ int ret;
+
+ if (!f->is_opened || f->is_dir)
+ return -P9_EPROTO;
+ ret = pwrite(f->u.fd, buf, count, offset);
+ if (ret < 0)
+ return -errno_to_p9(errno);
+ else
+ return ret;
+}
+
+static void fs_close(FSDevice *fs, FSFile *f)
+{
+ if (!f->is_opened)
+ return;
+ if (f->is_dir)
+ closedir(f->u.dirp);
+ else
+ close(f->u.fd);
+ f->is_opened = FALSE;
+}
+
+static int fs_stat(FSDevice *fs, FSFile *f, FSStat *st)
+{
+ struct stat st1;
+
+ if (lstat(f->path, &st1) != 0)
+ return -P9_ENOENT;
+ stat_to_qid(&st->qid, &st1);
+ st->st_mode = st1.st_mode;
+ st->st_uid = st1.st_uid;
+ st->st_gid = st1.st_gid;
+ st->st_nlink = st1.st_nlink;
+ st->st_rdev = st1.st_rdev;
+ st->st_size = st1.st_size;
+ st->st_blksize = st1.st_blksize;
+ st->st_blocks = st1.st_blocks;
+ st->st_atime_sec = st1.st_atim.tv_sec;
+ st->st_atime_nsec = st1.st_atim.tv_nsec;
+ st->st_mtime_sec = st1.st_mtim.tv_sec;
+ st->st_mtime_nsec = st1.st_mtim.tv_nsec;
+ st->st_ctime_sec = st1.st_ctim.tv_sec;
+ st->st_ctime_nsec = st1.st_ctim.tv_nsec;
+ return 0;
+}
+
+static int fs_setattr(FSDevice *fs, FSFile *f, uint32_t mask,
+ uint32_t mode, uint32_t uid, uint32_t gid,
+ uint64_t size, uint64_t atime_sec, uint64_t atime_nsec,
+ uint64_t mtime_sec, uint64_t mtime_nsec)
+{
+ BOOL ctime_updated = FALSE;
+
+ if (mask & (P9_SETATTR_UID | P9_SETATTR_GID)) {
+ if (lchown(f->path, (mask & P9_SETATTR_UID) ? uid : -1,
+ (mask & P9_SETATTR_GID) ? gid : -1) < 0)
+ return -errno_to_p9(errno);
+ ctime_updated = TRUE;
+ }
+ /* must be done after uid change for suid */
+ if (mask & P9_SETATTR_MODE) {
+ if (chmod(f->path, mode) < 0)
+ return -errno_to_p9(errno);
+ ctime_updated = TRUE;
+ }
+ if (mask & P9_SETATTR_SIZE) {
+ if (truncate(f->path, size) < 0)
+ return -errno_to_p9(errno);
+ ctime_updated = TRUE;
+ }
+ if (mask & (P9_SETATTR_ATIME | P9_SETATTR_MTIME)) {
+ struct timespec ts[2];
+ if (mask & P9_SETATTR_ATIME) {
+ if (mask & P9_SETATTR_ATIME_SET) {
+ ts[0].tv_sec = atime_sec;
+ ts[0].tv_nsec = atime_nsec;
+ } else {
+ ts[0].tv_sec = 0;
+ ts[0].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ ts[0].tv_sec = 0;
+ ts[0].tv_nsec = UTIME_OMIT;
+ }
+ if (mask & P9_SETATTR_MTIME) {
+ if (mask & P9_SETATTR_MTIME_SET) {
+ ts[1].tv_sec = mtime_sec;
+ ts[1].tv_nsec = mtime_nsec;
+ } else {
+ ts[1].tv_sec = 0;
+ ts[1].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ ts[1].tv_sec = 0;
+ ts[1].tv_nsec = UTIME_OMIT;
+ }
+ if (utimensat(AT_FDCWD, f->path, ts, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno_to_p9(errno);
+ ctime_updated = TRUE;
+ }
+ if ((mask & P9_SETATTR_CTIME) && !ctime_updated) {
+ if (lchown(f->path, -1, -1) < 0)
+ return -errno_to_p9(errno);
+ }
+ return 0;
+}
+
+static int fs_link(FSDevice *fs, FSFile *df, FSFile *f, const char *name)
+{
+ char *path;
+
+ path = compose_path(df->path, name);
+ if (link(f->path, path) < 0) {
+ free(path);
+ return -errno_to_p9(errno);
+ }
+ free(path);
+ return 0;
+}
+
+static int fs_symlink(FSDevice *fs, FSQID *qid,
+ FSFile *f, const char *name, const char *symgt, uint32_t gid)
+{
+ char *path;
+ struct stat st;
+
+ path = compose_path(f->path, name);
+ if (symlink(symgt, path) < 0) {
+ free(path);
+ return -errno_to_p9(errno);
+ }
+ if (lstat(path, &st) != 0) {
+ free(path);
+ return -errno_to_p9(errno);
+ }
+ free(path);
+ stat_to_qid(qid, &st);
+ return 0;
+}
+
+static int fs_mknod(FSDevice *fs, FSQID *qid,
+ FSFile *f, const char *name, uint32_t mode, uint32_t major,
+ uint32_t minor, uint32_t gid)
+{
+ char *path;
+ struct stat st;
+
+ path = compose_path(f->path, name);
+ if (mknod(path, mode, makedev(major, minor)) < 0) {
+ free(path);
+ return -errno_to_p9(errno);
+ }
+ if (lstat(path, &st) != 0) {
+ free(path);
+ return -errno_to_p9(errno);
+ }
+ free(path);
+ stat_to_qid(qid, &st);
+ return 0;
+}
+
+static int fs_readlink(FSDevice *fs, char *buf, int buf_size, FSFile *f)
+{
+ int ret;
+ ret = readlink(f->path, buf, buf_size - 1);
+ if (ret < 0)
+ return -errno_to_p9(errno);
+ buf[ret] = '\0';
+ return 0;
+}
+
+static int fs_renameat(FSDevice *fs, FSFile *f, const char *name,
+ FSFile *new_f, const char *new_name)
+{
+ char *path, *new_path;
+ int ret;
+
+ path = compose_path(f->path, name);
+ new_path = compose_path(new_f->path, new_name);
+ ret = rename(path, new_path);
+ free(path);
+ free(new_path);
+ if (ret < 0)
+ return -errno_to_p9(errno);
+ return 0;
+}
+
+static int fs_unlinkat(FSDevice *fs, FSFile *f, const char *name)
+{
+ char *path;
+ int ret;
+
+ path = compose_path(f->path, name);
+ ret = remove(path);
+ free(path);
+ if (ret < 0)
+ return -errno_to_p9(errno);
+ return 0;
+
+}
+
+static int fs_lock(FSDevice *fs, FSFile *f, const FSLock *lock)
+{
+ int ret;
+ struct flock fl;
+
+ /* XXX: lock directories too */
+ if (!f->is_opened || f->is_dir)
+ return -P9_EPROTO;
+
+ fl.l_type = lock->type;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = lock->start;
+ fl.l_len = lock->length;
+
+ ret = fcntl(f->u.fd, F_SETLK, &fl);
+ if (ret == 0) {
+ ret = P9_LOCK_SUCCESS;
+ } else if (errno == EAGAIN || errno == EACCES) {
+ ret = P9_LOCK_BLOCKED;
+ } else {
+ ret = -errno_to_p9(errno);
+ }
+ return ret;
+}
+
+static int fs_getlock(FSDevice *fs, FSFile *f, FSLock *lock)
+{
+ int ret;
+ struct flock fl;
+
+ /* XXX: lock directories too */
+ if (!f->is_opened || f->is_dir)
+ return -P9_EPROTO;
+
+ fl.l_type = lock->type;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = lock->start;
+ fl.l_len = lock->length;
+
+ ret = fcntl(f->u.fd, F_GETLK, &fl);
+ if (ret < 0) {
+ ret = -errno_to_p9(errno);
+ } else {
+ lock->type = fl.l_type;
+ lock->start = fl.l_start;
+ lock->length = fl.l_len;
+ }
+ return ret;
+}
+
+static void fs_disk_end(FSDevice *fs1)
+{
+ FSDeviceDisk *fs = (FSDeviceDisk *)fs1;
+ free(fs->root_path);
+}
+
+FSDevice *fs_disk_init(const char *root_path)
+{
+ FSDeviceDisk *fs;
+ struct stat st;
+
+ lstat(root_path, &st);
+ if (!S_ISDIR(st.st_mode))
+ return NULL;
+
+ fs = mallocz(sizeof(*fs));
+
+ fs->common.fs_end = fs_disk_end;
+ fs->common.fs_delete = fs_delete;
+ fs->common.fs_statfs = fs_statfs;
+ fs->common.fs_attach = fs_attach;
+ fs->common.fs_walk = fs_walk;
+ fs->common.fs_mkdir = fs_mkdir;
+ fs->common.fs_open = fs_open;
+ fs->common.fs_create = fs_create;
+ fs->common.fs_stat = fs_stat;
+ fs->common.fs_setattr = fs_setattr;
+ fs->common.fs_close = fs_close;
+ fs->common.fs_readdir = fs_readdir;
+ fs->common.fs_read = fs_read;
+ fs->common.fs_write = fs_write;
+ fs->common.fs_link = fs_link;
+ fs->common.fs_symlink = fs_symlink;
+ fs->common.fs_mknod = fs_mknod;
+ fs->common.fs_readlink = fs_readlink;
+ fs->common.fs_renameat = fs_renameat;
+ fs->common.fs_unlinkat = fs_unlinkat;
+ fs->common.fs_lock = fs_lock;
+ fs->common.fs_getlock = fs_getlock;
+
+ fs->root_path = strdup(root_path);
+ return (FSDevice *)fs;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs_net.c b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_net.c
new file mode 100644
index 0000000..c7c7484
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_net.c
@@ -0,0 +1,2910 @@
+/*
+ * Networked Filesystem using HTTP
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <ctype.h>
+
+#include "cutils.h"
+#include "list.h"
+#include "fs.h"
+#include "fs_utils.h"
+#include "fs_wget.h"
+#include "fbuf.h"
+
+#if defined(EMSCRIPTEN)
+#include <emscripten.h>
+#endif
+
+/*
+ TODO:
+ - implement fs_lock/fs_getlock
+ - update fs_size with links ?
+ - limit fs_size in dirent creation
+ - limit filename length
+*/
+
+//#define DEBUG_CACHE
+#if !defined(EMSCRIPTEN)
+#define DUMP_CACHE_LOAD
+#endif
+
+#if defined(EMSCRIPTEN)
+#define DEFAULT_INODE_CACHE_SIZE (64 * 1024 * 1024)
+#else
+#define DEFAULT_INODE_CACHE_SIZE (256 * 1024 * 1024)
+#endif
+
+typedef enum {
+ FT_FIFO = 1,
+ FT_CHR = 2,
+ FT_DIR = 4,
+ FT_BLK = 6,
+ FT_REG = 8,
+ FT_LNK = 10,
+ FT_SOCK = 12,
+} FSINodeTypeEnum;
+
+typedef enum {
+ REG_STATE_LOCAL, /* local content */
+ REG_STATE_UNLOADED, /* content not loaded */
+ REG_STATE_LOADING, /* content is being loaded */
+ REG_STATE_LOADED, /* loaded, not modified, stored in cached_inode_list */
+} FSINodeRegStateEnum;
+
+typedef struct FSBaseURL {
+ struct list_head link;
+ int ref_count;
+ char *base_url_id;
+ char *url;
+ char *user;
+ char *password;
+ BOOL encrypted;
+ AES_KEY aes_state;
+} FSBaseURL;
+
+typedef struct FSINode {
+ struct list_head link;
+ uint64_t inode_num; /* inode number */
+ int32_t refcount;
+ int32_t open_count;
+ FSINodeTypeEnum type;
+ uint32_t mode;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t mtime_sec;
+ uint32_t ctime_sec;
+ uint32_t mtime_nsec;
+ uint32_t ctime_nsec;
+ union {
+ struct {
+ FSINodeRegStateEnum state;
+ size_t size; /* real file size */
+ FileBuffer fbuf;
+ FSBaseURL *base_url;
+ FSFileID file_id; /* network file ID */
+ struct list_head link;
+ struct FSOpenInfo *open_info; /* used in LOADING state */
+ BOOL is_fscmd;
+#ifdef DUMP_CACHE_LOAD
+ char *filename;
+#endif
+ } reg;
+ struct {
+ struct list_head de_list; /* list of FSDirEntry */
+ int size;
+ } dir;
+ struct {
+ uint32_t major;
+ uint32_t minor;
+ } dev;
+ struct {
+ char *name;
+ } symlink;
+ } u;
+} FSINode;
+
+typedef struct {
+ struct list_head link;
+ FSINode *inode;
+ uint8_t mark; /* temporary use only */
+ char name[0];
+} FSDirEntry;
+
+typedef enum {
+ FS_CMD_XHR,
+ FS_CMD_PBKDF2,
+} FSCMDRequestEnum;
+
+#define FS_CMD_REPLY_LEN_MAX 64
+
+typedef struct {
+ FSCMDRequestEnum type;
+ struct CmdXHRState *xhr_state;
+ int reply_len;
+ uint8_t reply_buf[FS_CMD_REPLY_LEN_MAX];
+} FSCMDRequest;
+
+struct FSFile {
+ uint32_t uid;
+ FSINode *inode;
+ BOOL is_opened;
+ uint32_t open_flags;
+ FSCMDRequest *req;
+};
+
+typedef struct {
+ struct list_head link;
+ BOOL is_archive;
+ const char *name;
+} PreloadFile;
+
+typedef struct {
+ struct list_head link;
+ FSFileID file_id;
+ struct list_head file_list; /* list of PreloadFile.link */
+} PreloadEntry;
+
+typedef struct {
+ struct list_head link;
+ FSFileID file_id;
+ uint64_t size;
+ const char *name;
+} PreloadArchiveFile;
+
+typedef struct {
+ struct list_head link;
+ const char *name;
+ struct list_head file_list; /* list of PreloadArchiveFile.link */
+} PreloadArchive;
+
+typedef struct FSDeviceMem {
+ FSDevice common;
+
+ struct list_head inode_list; /* list of FSINode */
+ int64_t inode_count; /* current number of inodes */
+ uint64_t inode_limit;
+ int64_t fs_blocks;
+ uint64_t fs_max_blocks;
+ uint64_t inode_num_alloc;
+ int block_size_log2;
+ uint32_t block_size; /* for stat/statfs */
+ FSINode *root_inode;
+ struct list_head inode_cache_list; /* list of FSINode.u.reg.link */
+ int64_t inode_cache_size;
+ int64_t inode_cache_size_limit;
+ struct list_head preload_list; /* list of PreloadEntry.link */
+ struct list_head preload_archive_list; /* list of PreloadArchive.link */
+ /* network */
+ struct list_head base_url_list; /* list of FSBaseURL.link */
+ char *import_dir;
+#ifdef DUMP_CACHE_LOAD
+ BOOL dump_cache_load;
+ BOOL dump_started;
+ char *dump_preload_dir;
+ FILE *dump_preload_file;
+ FILE *dump_preload_archive_file;
+
+ char *dump_archive_name;
+ uint64_t dump_archive_size;
+ FILE *dump_archive_file;
+
+ int dump_archive_num;
+ struct list_head dump_preload_list; /* list of PreloadFile.link */
+ struct list_head dump_exclude_list; /* list of PreloadFile.link */
+#endif
+} FSDeviceMem;
+
+typedef enum {
+ FS_OPEN_WGET_REG,
+ FS_OPEN_WGET_ARCHIVE,
+ FS_OPEN_WGET_ARCHIVE_FILE,
+} FSOpenWgetEnum;
+
+typedef struct FSOpenInfo {
+ FSDevice *fs;
+ FSOpenWgetEnum open_type;
+
+ /* used for FS_OPEN_WGET_REG, FS_OPEN_WGET_ARCHIVE */
+ XHRState *xhr;
+ FSINode *n;
+ DecryptFileState *dec_state;
+ size_t cur_pos;
+
+ struct list_head archive_link; /* FS_OPEN_WGET_ARCHIVE_FILE */
+ uint64_t archive_offset; /* FS_OPEN_WGET_ARCHIVE_FILE */
+ struct list_head archive_file_list; /* FS_OPEN_WGET_ARCHIVE */
+
+ /* the following is set in case there is a fs_open callback */
+ FSFile *f;
+ FSOpenCompletionFunc *cb;
+ void *opaque;
+} FSOpenInfo;
+
+static void fs_close(FSDevice *fs, FSFile *f);
+static void inode_decref(FSDevice *fs1, FSINode *n);
+static int fs_cmd_write(FSDevice *fs, FSFile *f, uint64_t offset,
+ const uint8_t *buf, int buf_len);
+static int fs_cmd_read(FSDevice *fs, FSFile *f, uint64_t offset,
+ uint8_t *buf, int buf_len);
+static int fs_truncate(FSDevice *fs1, FSINode *n, uint64_t size);
+static void fs_open_end(FSOpenInfo *oi);
+static void fs_base_url_decref(FSDevice *fs, FSBaseURL *bu);
+static FSBaseURL *fs_net_set_base_url(FSDevice *fs1,
+ const char *base_url_id,
+ const char *url,
+ const char *user, const char *password,
+ AES_KEY *aes_state);
+static void fs_cmd_close(FSDevice *fs, FSFile *f);
+static void fs_error_archive(FSOpenInfo *oi);
+#ifdef DUMP_CACHE_LOAD
+static void dump_loaded_file(FSDevice *fs1, FSINode *n);
+#endif
+
+#if !defined(EMSCRIPTEN)
+/* file buffer (the content of the buffer can be stored elsewhere) */
+void file_buffer_init(FileBuffer *bs)
+{
+ bs->data = NULL;
+ bs->allocated_size = 0;
+}
+
+void file_buffer_reset(FileBuffer *bs)
+{
+ free(bs->data);
+ file_buffer_init(bs);
+}
+
+int file_buffer_resize(FileBuffer *bs, size_t new_size)
+{
+ uint8_t *new_data;
+ new_data = realloc(bs->data, new_size);
+ if (!new_data && new_size != 0)
+ return -1;
+ bs->data = new_data;
+ bs->allocated_size = new_size;
+ return 0;
+}
+
+void file_buffer_write(FileBuffer *bs, size_t offset, const uint8_t *buf,
+ size_t size)
+{
+ memcpy(bs->data + offset, buf, size);
+}
+
+void file_buffer_set(FileBuffer *bs, size_t offset, int val, size_t size)
+{
+ memset(bs->data + offset, val, size);
+}
+
+void file_buffer_read(FileBuffer *bs, size_t offset, uint8_t *buf,
+ size_t size)
+{
+ memcpy(buf, bs->data + offset, size);
+}
+#endif
+
+static int64_t to_blocks(FSDeviceMem *fs, uint64_t size)
+{
+ return (size + fs->block_size - 1) >> fs->block_size_log2;
+}
+
+static FSINode *inode_incref(FSDevice *fs, FSINode *n)
+{
+ n->refcount++;
+ return n;
+}
+
+static FSINode *inode_inc_open(FSDevice *fs, FSINode *n)
+{
+ n->open_count++;
+ return n;
+}
+
+static void inode_free(FSDevice *fs1, FSINode *n)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+
+ // printf("inode_free=%" PRId64 "\n", n->inode_num);
+ assert(n->refcount == 0);
+ assert(n->open_count == 0);
+ switch(n->type) {
+ case FT_REG:
+ fs->fs_blocks -= to_blocks(fs, n->u.reg.size);
+ assert(fs->fs_blocks >= 0);
+ file_buffer_reset(&n->u.reg.fbuf);
+#ifdef DUMP_CACHE_LOAD
+ free(n->u.reg.filename);
+#endif
+ switch(n->u.reg.state) {
+ case REG_STATE_LOADED:
+ list_del(&n->u.reg.link);
+ fs->inode_cache_size -= n->u.reg.size;
+ assert(fs->inode_cache_size >= 0);
+ fs_base_url_decref(fs1, n->u.reg.base_url);
+ break;
+ case REG_STATE_LOADING:
+ {
+ FSOpenInfo *oi = n->u.reg.open_info;
+ if (oi->xhr)
+ fs_wget_free(oi->xhr);
+ if (oi->open_type == FS_OPEN_WGET_ARCHIVE) {
+ fs_error_archive(oi);
+ }
+ fs_open_end(oi);
+ fs_base_url_decref(fs1, n->u.reg.base_url);
+ }
+ break;
+ case REG_STATE_UNLOADED:
+ fs_base_url_decref(fs1, n->u.reg.base_url);
+ break;
+ case REG_STATE_LOCAL:
+ break;
+ default:
+ abort();
+ }
+ break;
+ case FT_LNK:
+ free(n->u.symlink.name);
+ break;
+ case FT_DIR:
+ assert(list_empty(&n->u.dir.de_list));
+ break;
+ default:
+ break;
+ }
+ list_del(&n->link);
+ free(n);
+ fs->inode_count--;
+ assert(fs->inode_count >= 0);
+}
+
+static void inode_decref(FSDevice *fs1, FSINode *n)
+{
+ assert(n->refcount >= 1);
+ if (--n->refcount <= 0 && n->open_count <= 0) {
+ inode_free(fs1, n);
+ }
+}
+
+static void inode_dec_open(FSDevice *fs1, FSINode *n)
+{
+ assert(n->open_count >= 1);
+ if (--n->open_count <= 0 && n->refcount <= 0) {
+ inode_free(fs1, n);
+ }
+}
+
+static void inode_update_mtime(FSDevice *fs, FSINode *n)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ n->mtime_sec = tv.tv_sec;
+ n->mtime_nsec = tv.tv_usec * 1000;
+}
+
+static FSINode *inode_new(FSDevice *fs1, FSINodeTypeEnum type,
+ uint32_t mode, uint32_t uid, uint32_t gid)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ FSINode *n;
+
+ n = mallocz(sizeof(*n));
+ n->refcount = 1;
+ n->open_count = 0;
+ n->inode_num = fs->inode_num_alloc;
+ fs->inode_num_alloc++;
+ n->type = type;
+ n->mode = mode & 0xfff;
+ n->uid = uid;
+ n->gid = gid;
+
+ switch(type) {
+ case FT_REG:
+ file_buffer_init(&n->u.reg.fbuf);
+ break;
+ case FT_DIR:
+ init_list_head(&n->u.dir.de_list);
+ break;
+ default:
+ break;
+ }
+
+ list_add(&n->link, &fs->inode_list);
+ fs->inode_count++;
+
+ inode_update_mtime(fs1, n);
+ n->ctime_sec = n->mtime_sec;
+ n->ctime_nsec = n->mtime_nsec;
+
+ return n;
+}
+
+/* warning: the refcount of 'n1' is not incremented by this function */
+/* XXX: test FS max size */
+static FSDirEntry *inode_dir_add(FSDevice *fs1, FSINode *n, const char *name,
+ FSINode *n1)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ FSDirEntry *de;
+ int name_len, dirent_size, new_size;
+ assert(n->type == FT_DIR);
+
+ name_len = strlen(name);
+ de = mallocz(sizeof(*de) + name_len + 1);
+ de->inode = n1;
+ memcpy(de->name, name, name_len + 1);
+ dirent_size = sizeof(*de) + name_len + 1;
+ new_size = n->u.dir.size + dirent_size;
+ fs->fs_blocks += to_blocks(fs, new_size) - to_blocks(fs, n->u.dir.size);
+ n->u.dir.size = new_size;
+ list_add_tail(&de->link, &n->u.dir.de_list);
+ return de;
+}
+
+static FSDirEntry *inode_search(FSINode *n, const char *name)
+{
+ struct list_head *el;
+ FSDirEntry *de;
+
+ if (n->type != FT_DIR)
+ return NULL;
+
+ list_for_each(el, &n->u.dir.de_list) {
+ de = list_entry(el, FSDirEntry, link);
+ if (!strcmp(de->name, name))
+ return de;
+ }
+ return NULL;
+}
+
+static FSINode *inode_search_path1(FSDevice *fs, FSINode *n, const char *path)
+{
+ char name[1024];
+ const char *p, *p1;
+ int len;
+ FSDirEntry *de;
+
+ p = path;
+ if (*p == '/')
+ p++;
+ if (*p == '\0')
+ return n;
+ for(;;) {
+ p1 = strchr(p, '/');
+ if (!p1) {
+ len = strlen(p);
+ } else {
+ len = p1 - p;
+ p1++;
+ }
+ if (len > sizeof(name) - 1)
+ return NULL;
+ memcpy(name, p, len);
+ name[len] = '\0';
+ if (n->type != FT_DIR)
+ return NULL;
+ de = inode_search(n, name);
+ if (!de)
+ return NULL;
+ n = de->inode;
+ p = p1;
+ if (!p)
+ break;
+ }
+ return n;
+}
+
+static FSINode *inode_search_path(FSDevice *fs1, const char *path)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ if (!fs1)
+ return NULL;
+ return inode_search_path1(fs1, fs->root_inode, path);
+}
+
+static BOOL is_empty_dir(FSDevice *fs, FSINode *n)
+{
+ struct list_head *el;
+ FSDirEntry *de;
+
+ list_for_each(el, &n->u.dir.de_list) {
+ de = list_entry(el, FSDirEntry, link);
+ if (strcmp(de->name, ".") != 0 &&
+ strcmp(de->name, "..") != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void inode_dirent_delete_no_decref(FSDevice *fs1, FSINode *n, FSDirEntry *de)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ int dirent_size, new_size;
+ dirent_size = sizeof(*de) + strlen(de->name) + 1;
+
+ new_size = n->u.dir.size - dirent_size;
+ fs->fs_blocks += to_blocks(fs, new_size) - to_blocks(fs, n->u.dir.size);
+ n->u.dir.size = new_size;
+ assert(n->u.dir.size >= 0);
+ assert(fs->fs_blocks >= 0);
+ list_del(&de->link);
+ free(de);
+}
+
+static void inode_dirent_delete(FSDevice *fs, FSINode *n, FSDirEntry *de)
+{
+ FSINode *n1;
+ n1 = de->inode;
+ inode_dirent_delete_no_decref(fs, n, de);
+ inode_decref(fs, n1);
+}
+
+static void flush_dir(FSDevice *fs, FSINode *n)
+{
+ struct list_head *el, *el1;
+ FSDirEntry *de;
+ list_for_each_safe(el, el1, &n->u.dir.de_list) {
+ de = list_entry(el, FSDirEntry, link);
+ inode_dirent_delete(fs, n, de);
+ }
+ assert(n->u.dir.size == 0);
+}
+
+static void fs_delete(FSDevice *fs, FSFile *f)
+{
+ fs_close(fs, f);
+ inode_dec_open(fs, f->inode);
+ free(f);
+}
+
+static FSFile *fid_create(FSDevice *fs1, FSINode *n, uint32_t uid)
+{
+ FSFile *f;
+
+ f = mallocz(sizeof(*f));
+ f->inode = inode_inc_open(fs1, n);
+ f->uid = uid;
+ return f;
+}
+
+static void inode_to_qid(FSQID *qid, FSINode *n)
+{
+ if (n->type == FT_DIR)
+ qid->type = P9_QTDIR;
+ else if (n->type == FT_LNK)
+ qid->type = P9_QTSYMLINK;
+ else
+ qid->type = P9_QTFILE;
+ qid->version = 0; /* no caching on client */
+ qid->path = n->inode_num;
+}
+
+static void fs_statfs(FSDevice *fs1, FSStatFS *st)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ st->f_bsize = 1024;
+ st->f_blocks = fs->fs_max_blocks <<
+ (fs->block_size_log2 - 10);
+ st->f_bfree = (fs->fs_max_blocks - fs->fs_blocks) <<
+ (fs->block_size_log2 - 10);
+ st->f_bavail = st->f_bfree;
+ st->f_files = fs->inode_limit;
+ st->f_ffree = fs->inode_limit - fs->inode_count;
+}
+
+static int fs_attach(FSDevice *fs1, FSFile **pf, FSQID *qid, uint32_t uid,
+ const char *uname, const char *aname)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+
+ *pf = fid_create(fs1, fs->root_inode, uid);
+ inode_to_qid(qid, fs->root_inode);
+ return 0;
+}
+
+static int fs_walk(FSDevice *fs, FSFile **pf, FSQID *qids,
+ FSFile *f, int count, char **names)
+{
+ int i;
+ FSINode *n;
+ FSDirEntry *de;
+
+ n = f->inode;
+ for(i = 0; i < count; i++) {
+ de = inode_search(n, names[i]);
+ if (!de)
+ break;
+ n = de->inode;
+ inode_to_qid(&qids[i], n);
+ }
+ *pf = fid_create(fs, n, f->uid);
+ return i;
+}
+
+static int fs_mkdir(FSDevice *fs, FSQID *qid, FSFile *f,
+ const char *name, uint32_t mode, uint32_t gid)
+{
+ FSINode *n, *n1;
+
+ n = f->inode;
+ if (n->type != FT_DIR)
+ return -P9_ENOTDIR;
+ if (inode_search(n, name))
+ return -P9_EEXIST;
+ n1 = inode_new(fs, FT_DIR, mode, f->uid, gid);
+ inode_dir_add(fs, n1, ".", inode_incref(fs, n1));
+ inode_dir_add(fs, n1, "..", inode_incref(fs, n));
+ inode_dir_add(fs, n, name, n1);
+ inode_to_qid(qid, n1);
+ return 0;
+}
+
+/* remove elements in the cache considering that 'added_size' will be
+ added */
+static void fs_trim_cache(FSDevice *fs1, int64_t added_size)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ struct list_head *el, *el1;
+ FSINode *n;
+
+ if ((fs->inode_cache_size + added_size) <= fs->inode_cache_size_limit)
+ return;
+ list_for_each_prev_safe(el, el1, &fs->inode_cache_list) {
+ n = list_entry(el, FSINode, u.reg.link);
+ assert(n->u.reg.state == REG_STATE_LOADED);
+ /* cannot remove open files */
+ // printf("open_count=%d\n", n->open_count);
+ if (n->open_count != 0)
+ continue;
+#ifdef DEBUG_CACHE
+ printf("fs_trim_cache: remove '%s' size=%ld\n",
+ n->u.reg.filename, (long)n->u.reg.size);
+#endif
+ file_buffer_reset(&n->u.reg.fbuf);
+ n->u.reg.state = REG_STATE_UNLOADED;
+ list_del(&n->u.reg.link);
+ fs->inode_cache_size -= n->u.reg.size;
+ assert(fs->inode_cache_size >= 0);
+ if ((fs->inode_cache_size + added_size) <= fs->inode_cache_size_limit)
+ break;
+ }
+}
+
+static void fs_open_end(FSOpenInfo *oi)
+{
+ if (oi->open_type == FS_OPEN_WGET_ARCHIVE_FILE) {
+ list_del(&oi->archive_link);
+ }
+ if (oi->dec_state)
+ decrypt_file_end(oi->dec_state);
+ free(oi);
+}
+
+static int fs_open_write_cb(void *opaque, const uint8_t *data, size_t size)
+{
+ FSOpenInfo *oi = opaque;
+ size_t len;
+ FSINode *n = oi->n;
+
+ /* we ignore extraneous data */
+ len = n->u.reg.size - oi->cur_pos;
+ if (size < len)
+ len = size;
+ file_buffer_write(&n->u.reg.fbuf, oi->cur_pos, data, len);
+ oi->cur_pos += len;
+ return 0;
+}
+
+static void fs_wget_set_loaded(FSINode *n)
+{
+ FSOpenInfo *oi;
+ FSDeviceMem *fs;
+ FSFile *f;
+ FSQID qid;
+
+ assert(n->u.reg.state == REG_STATE_LOADING);
+ oi = n->u.reg.open_info;
+ fs = (FSDeviceMem *)oi->fs;
+ n->u.reg.state = REG_STATE_LOADED;
+ list_add(&n->u.reg.link, &fs->inode_cache_list);
+ fs->inode_cache_size += n->u.reg.size;
+
+ if (oi->cb) {
+ f = oi->f;
+ f->is_opened = TRUE;
+ inode_to_qid(&qid, n);
+ oi->cb(oi->fs, &qid, 0, oi->opaque);
+ }
+ fs_open_end(oi);
+}
+
+static void fs_wget_set_error(FSINode *n)
+{
+ FSOpenInfo *oi;
+ assert(n->u.reg.state == REG_STATE_LOADING);
+ oi = n->u.reg.open_info;
+ n->u.reg.state = REG_STATE_UNLOADED;
+ file_buffer_reset(&n->u.reg.fbuf);
+ if (oi->cb) {
+ oi->cb(oi->fs, NULL, -P9_EIO, oi->opaque);
+ }
+ fs_open_end(oi);
+}
+
+static void fs_read_archive(FSOpenInfo *oi)
+{
+ FSINode *n = oi->n;
+ uint64_t pos, pos1, l;
+ uint8_t buf[1024];
+ FSINode *n1;
+ FSOpenInfo *oi1;
+ struct list_head *el, *el1;
+
+ list_for_each_safe(el, el1, &oi->archive_file_list) {
+ oi1 = list_entry(el, FSOpenInfo, archive_link);
+ n1 = oi1->n;
+ /* copy the archive data to the file */
+ pos = oi1->archive_offset;
+ pos1 = 0;
+ while (pos1 < n1->u.reg.size) {
+ l = n1->u.reg.size - pos1;
+ if (l > sizeof(buf))
+ l = sizeof(buf);
+ file_buffer_read(&n->u.reg.fbuf, pos, buf, l);
+ file_buffer_write(&n1->u.reg.fbuf, pos1, buf, l);
+ pos += l;
+ pos1 += l;
+ }
+ fs_wget_set_loaded(n1);
+ }
+}
+
+static void fs_error_archive(FSOpenInfo *oi)
+{
+ FSOpenInfo *oi1;
+ struct list_head *el, *el1;
+
+ list_for_each_safe(el, el1, &oi->archive_file_list) {
+ oi1 = list_entry(el, FSOpenInfo, archive_link);
+ fs_wget_set_error(oi1->n);
+ }
+}
+
+static void fs_open_cb(void *opaque, int err, void *data, size_t size)
+{
+ FSOpenInfo *oi = opaque;
+ FSINode *n = oi->n;
+
+ // printf("open_cb: err=%d size=%ld\n", err, size);
+ if (err < 0) {
+ error:
+ if (oi->open_type == FS_OPEN_WGET_ARCHIVE)
+ fs_error_archive(oi);
+ fs_wget_set_error(n);
+ } else {
+ if (oi->dec_state) {
+ if (decrypt_file(oi->dec_state, data, size) < 0)
+ goto error;
+ if (err == 0) {
+ if (decrypt_file_flush(oi->dec_state) < 0)
+ goto error;
+ }
+ } else {
+ fs_open_write_cb(oi, data, size);
+ }
+
+ if (err == 0) {
+ /* end of transfer */
+ if (oi->cur_pos != n->u.reg.size)
+ goto error;
+#ifdef DUMP_CACHE_LOAD
+ dump_loaded_file(oi->fs, n);
+#endif
+ if (oi->open_type == FS_OPEN_WGET_ARCHIVE)
+ fs_read_archive(oi);
+ fs_wget_set_loaded(n);
+ }
+ }
+}
+
+
+static int fs_open_wget(FSDevice *fs1, FSINode *n, FSOpenWgetEnum open_type)
+{
+ char *url;
+ FSOpenInfo *oi;
+ char fname[FILEID_SIZE_MAX];
+ FSBaseURL *bu;
+
+ assert(n->u.reg.state == REG_STATE_UNLOADED);
+
+ fs_trim_cache(fs1, n->u.reg.size);
+
+ if (file_buffer_resize(&n->u.reg.fbuf, n->u.reg.size) < 0)
+ return -P9_EIO;
+ n->u.reg.state = REG_STATE_LOADING;
+ oi = mallocz(sizeof(*oi));
+ oi->cur_pos = 0;
+ oi->fs = fs1;
+ oi->n = n;
+ oi->open_type = open_type;
+ if (open_type != FS_OPEN_WGET_ARCHIVE_FILE) {
+ if (open_type == FS_OPEN_WGET_ARCHIVE)
+ init_list_head(&oi->archive_file_list);
+ file_id_to_filename(fname, n->u.reg.file_id);
+ bu = n->u.reg.base_url;
+ url = compose_path(bu->url, fname);
+ if (bu->encrypted) {
+ oi->dec_state = decrypt_file_init(&bu->aes_state, fs_open_write_cb, oi);
+ }
+ oi->xhr = fs_wget(url, bu->user, bu->password, oi, fs_open_cb, FALSE);
+ }
+ n->u.reg.open_info = oi;
+ return 0;
+}
+
+
+static void fs_preload_file(FSDevice *fs1, const char *filename)
+{
+ FSINode *n;
+
+ n = inode_search_path(fs1, filename);
+ if (n && n->type == FT_REG && n->u.reg.state == REG_STATE_UNLOADED) {
+#if defined(DEBUG_CACHE)
+ printf("preload: %s\n", filename);
+#endif
+ fs_open_wget(fs1, n, FS_OPEN_WGET_REG);
+ }
+}
+
+static PreloadArchive *find_preload_archive(FSDeviceMem *fs,
+ const char *filename)
+{
+ PreloadArchive *pa;
+ struct list_head *el;
+ list_for_each(el, &fs->preload_archive_list) {
+ pa = list_entry(el, PreloadArchive, link);
+ if (!strcmp(pa->name, filename))
+ return pa;
+ }
+ return NULL;
+}
+
+static void fs_preload_archive(FSDevice *fs1, const char *filename)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ PreloadArchive *pa;
+ PreloadArchiveFile *paf;
+ struct list_head *el;
+ FSINode *n, *n1;
+ uint64_t offset;
+ BOOL has_unloaded;
+
+ pa = find_preload_archive(fs, filename);
+ if (!pa)
+ return;
+#if defined(DEBUG_CACHE)
+ printf("preload archive: %s\n", filename);
+#endif
+ n = inode_search_path(fs1, filename);
+ if (n && n->type == FT_REG && n->u.reg.state == REG_STATE_UNLOADED) {
+ /* if all the files are loaded, no need to load the archive */
+ offset = 0;
+ has_unloaded = FALSE;
+ list_for_each(el, &pa->file_list) {
+ paf = list_entry(el, PreloadArchiveFile, link);
+ n1 = inode_search_path(fs1, paf->name);
+ if (n1 && n1->type == FT_REG &&
+ n1->u.reg.state == REG_STATE_UNLOADED) {
+ has_unloaded = TRUE;
+ }
+ offset += paf->size;
+ }
+ if (!has_unloaded) {
+#if defined(DEBUG_CACHE)
+ printf("archive files already loaded\n");
+#endif
+ return;
+ }
+ /* check archive size consistency */
+ if (offset != n->u.reg.size) {
+#if defined(DEBUG_CACHE)
+ printf(" inconsistent archive size: %" PRId64 " %" PRId64 "\n",
+ offset, n->u.reg.size);
+#endif
+ goto load_fallback;
+ }
+
+ /* start loading the archive */
+ fs_open_wget(fs1, n, FS_OPEN_WGET_ARCHIVE);
+
+ /* indicate that all the archive files are being loaded. Also
+ check consistency of size and file id */
+ offset = 0;
+ list_for_each(el, &pa->file_list) {
+ paf = list_entry(el, PreloadArchiveFile, link);
+ n1 = inode_search_path(fs1, paf->name);
+ if (n1 && n1->type == FT_REG &&
+ n1->u.reg.state == REG_STATE_UNLOADED) {
+ if (n1->u.reg.size == paf->size &&
+ n1->u.reg.file_id == paf->file_id) {
+ fs_open_wget(fs1, n1, FS_OPEN_WGET_ARCHIVE_FILE);
+ list_add_tail(&n1->u.reg.open_info->archive_link,
+ &n->u.reg.open_info->archive_file_list);
+ n1->u.reg.open_info->archive_offset = offset;
+ } else {
+#if defined(DEBUG_CACHE)
+ printf(" inconsistent archive file: %s\n", paf->name);
+#endif
+ /* fallback to file preload */
+ fs_preload_file(fs1, paf->name);
+ }
+ }
+ offset += paf->size;
+ }
+ } else {
+ load_fallback:
+ /* if the archive is already loaded or not loaded, we load the
+ files separately (XXX: not optimal if the archive is
+ already loaded, but it should not happen often) */
+ list_for_each(el, &pa->file_list) {
+ paf = list_entry(el, PreloadArchiveFile, link);
+ fs_preload_file(fs1, paf->name);
+ }
+ }
+}
+
+static void fs_preload_files(FSDevice *fs1, FSFileID file_id)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ struct list_head *el;
+ PreloadEntry *pe;
+ PreloadFile *pf;
+
+ list_for_each(el, &fs->preload_list) {
+ pe = list_entry(el, PreloadEntry, link);
+ if (pe->file_id == file_id)
+ goto found;
+ }
+ return;
+ found:
+ list_for_each(el, &pe->file_list) {
+ pf = list_entry(el, PreloadFile, link);
+ if (pf->is_archive)
+ fs_preload_archive(fs1, pf->name);
+ else
+ fs_preload_file(fs1, pf->name);
+ }
+}
+
+/* return < 0 if error, 0 if OK, 1 if asynchronous completion */
+/* XXX: we don't support several simultaneous asynchronous open on the
+ same inode */
+static int fs_open(FSDevice *fs1, FSQID *qid, FSFile *f, uint32_t flags,
+ FSOpenCompletionFunc *cb, void *opaque)
+{
+ FSINode *n = f->inode;
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ int ret;
+
+ fs_close(fs1, f);
+
+ if (flags & P9_O_DIRECTORY) {
+ if (n->type != FT_DIR)
+ return -P9_ENOTDIR;
+ } else {
+ if (n->type != FT_REG && n->type != FT_DIR)
+ return -P9_EINVAL; /* XXX */
+ }
+ f->open_flags = flags;
+ if (n->type == FT_REG) {
+ if ((flags & P9_O_TRUNC) && (flags & P9_O_NOACCESS) != P9_O_RDONLY) {
+ fs_truncate(fs1, n, 0);
+ }
+
+ switch(n->u.reg.state) {
+ case REG_STATE_UNLOADED:
+ {
+ FSOpenInfo *oi;
+ /* need to load the file */
+ fs_preload_files(fs1, n->u.reg.file_id);
+ /* The state can be modified by the fs_preload_files */
+ if (n->u.reg.state == REG_STATE_LOADING)
+ goto handle_loading;
+ ret = fs_open_wget(fs1, n, FS_OPEN_WGET_REG);
+ if (ret)
+ return ret;
+ oi = n->u.reg.open_info;
+ oi->f = f;
+ oi->cb = cb;
+ oi->opaque = opaque;
+ return 1; /* completion callback will be called later */
+ }
+ break;
+ case REG_STATE_LOADING:
+ handle_loading:
+ {
+ FSOpenInfo *oi;
+ /* we only handle the case where the file is being preloaded */
+ oi = n->u.reg.open_info;
+ if (oi->cb)
+ return -P9_EIO;
+ oi = n->u.reg.open_info;
+ oi->f = f;
+ oi->cb = cb;
+ oi->opaque = opaque;
+ return 1; /* completion callback will be called later */
+ }
+ break;
+ case REG_STATE_LOCAL:
+ goto do_open;
+ case REG_STATE_LOADED:
+ /* move to front */
+ list_del(&n->u.reg.link);
+ list_add(&n->u.reg.link, &fs->inode_cache_list);
+ goto do_open;
+ default:
+ abort();
+ }
+ } else {
+ do_open:
+ f->is_opened = TRUE;
+ inode_to_qid(qid, n);
+ return 0;
+ }
+}
+
+static int fs_create(FSDevice *fs, FSQID *qid, FSFile *f, const char *name,
+ uint32_t flags, uint32_t mode, uint32_t gid)
+{
+ FSINode *n1, *n = f->inode;
+
+ if (n->type != FT_DIR)
+ return -P9_ENOTDIR;
+ if (inode_search(n, name)) {
+ /* XXX: support it, but Linux does not seem to use this case */
+ return -P9_EEXIST;
+ } else {
+ fs_close(fs, f);
+
+ n1 = inode_new(fs, FT_REG, mode, f->uid, gid);
+ inode_dir_add(fs, n, name, n1);
+
+ inode_dec_open(fs, f->inode);
+ f->inode = inode_inc_open(fs, n1);
+ f->is_opened = TRUE;
+ f->open_flags = flags;
+ inode_to_qid(qid, n1);
+ return 0;
+ }
+}
+
+static int fs_readdir(FSDevice *fs, FSFile *f, uint64_t offset1,
+ uint8_t *buf, int count)
+{
+ FSINode *n1, *n = f->inode;
+ int len, pos, name_len, type;
+ struct list_head *el;
+ FSDirEntry *de;
+ uint64_t offset;
+
+ if (!f->is_opened || n->type != FT_DIR)
+ return -P9_EPROTO;
+
+ el = n->u.dir.de_list.next;
+ offset = 0;
+ while (offset < offset1) {
+ if (el == &n->u.dir.de_list)
+ return 0; /* no more entries */
+ offset++;
+ el = el->next;
+ }
+
+ pos = 0;
+ for(;;) {
+ if (el == &n->u.dir.de_list)
+ break;
+ de = list_entry(el, FSDirEntry, link);
+ name_len = strlen(de->name);
+ len = 13 + 8 + 1 + 2 + name_len;
+ if ((pos + len) > count)
+ break;
+ offset++;
+ n1 = de->inode;
+ if (n1->type == FT_DIR)
+ type = P9_QTDIR;
+ else if (n1->type == FT_LNK)
+ type = P9_QTSYMLINK;
+ else
+ type = P9_QTFILE;
+ buf[pos++] = type;
+ put_le32(buf + pos, 0); /* version */
+ pos += 4;
+ put_le64(buf + pos, n1->inode_num);
+ pos += 8;
+ put_le64(buf + pos, offset);
+ pos += 8;
+ buf[pos++] = n1->type;
+ put_le16(buf + pos, name_len);
+ pos += 2;
+ memcpy(buf + pos, de->name, name_len);
+ pos += name_len;
+ el = el->next;
+ }
+ return pos;
+}
+
+static int fs_read(FSDevice *fs, FSFile *f, uint64_t offset,
+ uint8_t *buf, int count)
+{
+ FSINode *n = f->inode;
+ uint64_t count1;
+
+ if (!f->is_opened)
+ return -P9_EPROTO;
+ if (n->type != FT_REG)
+ return -P9_EIO;
+ if ((f->open_flags & P9_O_NOACCESS) == P9_O_WRONLY)
+ return -P9_EIO;
+ if (n->u.reg.is_fscmd)
+ return fs_cmd_read(fs, f, offset, buf, count);
+ if (offset >= n->u.reg.size)
+ return 0;
+ count1 = n->u.reg.size - offset;
+ if (count1 < count)
+ count = count1;
+ file_buffer_read(&n->u.reg.fbuf, offset, buf, count);
+ return count;
+}
+
+static int fs_truncate(FSDevice *fs1, FSINode *n, uint64_t size)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ intptr_t diff, diff_blocks;
+ size_t new_allocated_size;
+
+ if (n->type != FT_REG)
+ return -P9_EINVAL;
+ if (size > UINTPTR_MAX)
+ return -P9_ENOSPC;
+ diff = size - n->u.reg.size;
+ if (diff == 0)
+ return 0;
+ diff_blocks = to_blocks(fs, size) - to_blocks(fs, n->u.reg.size);
+ /* currently cannot resize while loading */
+ switch(n->u.reg.state) {
+ case REG_STATE_LOADING:
+ return -P9_EIO;
+ case REG_STATE_UNLOADED:
+ if (size == 0) {
+ /* now local content */
+ n->u.reg.state = REG_STATE_LOCAL;
+ }
+ break;
+ case REG_STATE_LOADED:
+ case REG_STATE_LOCAL:
+ if (diff > 0) {
+ if ((fs->fs_blocks + diff_blocks) > fs->fs_max_blocks)
+ return -P9_ENOSPC;
+ if (size > n->u.reg.fbuf.allocated_size) {
+ new_allocated_size = n->u.reg.fbuf.allocated_size * 5 / 4;
+ if (size > new_allocated_size)
+ new_allocated_size = size;
+ if (file_buffer_resize(&n->u.reg.fbuf, new_allocated_size) < 0)
+ return -P9_ENOSPC;
+ }
+ file_buffer_set(&n->u.reg.fbuf, n->u.reg.size, 0, diff);
+ } else {
+ new_allocated_size = n->u.reg.fbuf.allocated_size * 4 / 5;
+ if (size <= new_allocated_size) {
+ if (file_buffer_resize(&n->u.reg.fbuf, new_allocated_size) < 0)
+ return -P9_ENOSPC;
+ }
+ }
+ /* file is modified, so it is now local */
+ if (n->u.reg.state == REG_STATE_LOADED) {
+ list_del(&n->u.reg.link);
+ fs->inode_cache_size -= n->u.reg.size;
+ assert(fs->inode_cache_size >= 0);
+ n->u.reg.state = REG_STATE_LOCAL;
+ }
+ break;
+ default:
+ abort();
+ }
+ fs->fs_blocks += diff_blocks;
+ assert(fs->fs_blocks >= 0);
+ n->u.reg.size = size;
+ return 0;
+}
+
+static int fs_write(FSDevice *fs1, FSFile *f, uint64_t offset,
+ const uint8_t *buf, int count)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ FSINode *n = f->inode;
+ uint64_t end;
+ int err;
+
+ if (!f->is_opened)
+ return -P9_EPROTO;
+ if (n->type != FT_REG)
+ return -P9_EIO;
+ if ((f->open_flags & P9_O_NOACCESS) == P9_O_RDONLY)
+ return -P9_EIO;
+ if (count == 0)
+ return 0;
+ if (n->u.reg.is_fscmd) {
+ return fs_cmd_write(fs1, f, offset, buf, count);
+ }
+ end = offset + count;
+ if (end > n->u.reg.size) {
+ err = fs_truncate(fs1, n, end);
+ if (err)
+ return err;
+ }
+ inode_update_mtime(fs1, n);
+ /* file is modified, so it is now local */
+ if (n->u.reg.state == REG_STATE_LOADED) {
+ list_del(&n->u.reg.link);
+ fs->inode_cache_size -= n->u.reg.size;
+ assert(fs->inode_cache_size >= 0);
+ n->u.reg.state = REG_STATE_LOCAL;
+ }
+ file_buffer_write(&n->u.reg.fbuf, offset, buf, count);
+ return count;
+}
+
+static void fs_close(FSDevice *fs, FSFile *f)
+{
+ if (f->is_opened) {
+ f->is_opened = FALSE;
+ }
+ if (f->req)
+ fs_cmd_close(fs, f);
+}
+
+static int fs_stat(FSDevice *fs1, FSFile *f, FSStat *st)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ FSINode *n = f->inode;
+
+ inode_to_qid(&st->qid, n);
+ st->st_mode = n->mode | (n->type << 12);
+ st->st_uid = n->uid;
+ st->st_gid = n->gid;
+ st->st_nlink = n->refcount;
+ if (n->type == FT_BLK || n->type == FT_CHR) {
+ /* XXX: check */
+ st->st_rdev = (n->u.dev.major << 8) | n->u.dev.minor;
+ } else {
+ st->st_rdev = 0;
+ }
+ st->st_blksize = fs->block_size;
+ if (n->type == FT_REG) {
+ st->st_size = n->u.reg.size;
+ } else if (n->type == FT_LNK) {
+ st->st_size = strlen(n->u.symlink.name);
+ } else if (n->type == FT_DIR) {
+ st->st_size = n->u.dir.size;
+ } else {
+ st->st_size = 0;
+ }
+ /* in 512 byte blocks */
+ st->st_blocks = to_blocks(fs, st->st_size) << (fs->block_size_log2 - 9);
+
+ /* Note: atime is not supported */
+ st->st_atime_sec = n->mtime_sec;
+ st->st_atime_nsec = n->mtime_nsec;
+ st->st_mtime_sec = n->mtime_sec;
+ st->st_mtime_nsec = n->mtime_nsec;
+ st->st_ctime_sec = n->ctime_sec;
+ st->st_ctime_nsec = n->ctime_nsec;
+ return 0;
+}
+
+static int fs_setattr(FSDevice *fs1, FSFile *f, uint32_t mask,
+ uint32_t mode, uint32_t uid, uint32_t gid,
+ uint64_t size, uint64_t atime_sec, uint64_t atime_nsec,
+ uint64_t mtime_sec, uint64_t mtime_nsec)
+{
+ FSINode *n = f->inode;
+ int ret;
+
+ if (mask & P9_SETATTR_MODE) {
+ n->mode = mode;
+ }
+ if (mask & P9_SETATTR_UID) {
+ n->uid = uid;
+ }
+ if (mask & P9_SETATTR_GID) {
+ n->gid = gid;
+ }
+ if (mask & P9_SETATTR_SIZE) {
+ ret = fs_truncate(fs1, n, size);
+ if (ret)
+ return ret;
+ }
+ if (mask & P9_SETATTR_MTIME) {
+ if (mask & P9_SETATTR_MTIME_SET) {
+ n->mtime_sec = mtime_sec;
+ n->mtime_nsec = mtime_nsec;
+ } else {
+ inode_update_mtime(fs1, n);
+ }
+ }
+ if (mask & P9_SETATTR_CTIME) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ n->ctime_sec = tv.tv_sec;
+ n->ctime_nsec = tv.tv_usec * 1000;
+ }
+ return 0;
+}
+
+static int fs_link(FSDevice *fs, FSFile *df, FSFile *f, const char *name)
+{
+ FSINode *n = df->inode;
+
+ if (f->inode->type == FT_DIR)
+ return -P9_EPERM;
+ if (inode_search(n, name))
+ return -P9_EEXIST;
+ inode_dir_add(fs, n, name, inode_incref(fs, f->inode));
+ return 0;
+}
+
+static int fs_symlink(FSDevice *fs, FSQID *qid,
+ FSFile *f, const char *name, const char *symgt, uint32_t gid)
+{
+ FSINode *n1, *n = f->inode;
+
+ if (inode_search(n, name))
+ return -P9_EEXIST;
+
+ n1 = inode_new(fs, FT_LNK, 0777, f->uid, gid);
+ n1->u.symlink.name = strdup(symgt);
+ inode_dir_add(fs, n, name, n1);
+ inode_to_qid(qid, n1);
+ return 0;
+}
+
+static int fs_mknod(FSDevice *fs, FSQID *qid,
+ FSFile *f, const char *name, uint32_t mode, uint32_t major,
+ uint32_t minor, uint32_t gid)
+{
+ int type;
+ FSINode *n1, *n = f->inode;
+
+ type = (mode & P9_S_IFMT) >> 12;
+ /* XXX: add FT_DIR support */
+ if (type != FT_FIFO && type != FT_CHR && type != FT_BLK &&
+ type != FT_REG && type != FT_SOCK)
+ return -P9_EINVAL;
+ if (inode_search(n, name))
+ return -P9_EEXIST;
+ n1 = inode_new(fs, type, mode, f->uid, gid);
+ if (type == FT_CHR || type == FT_BLK) {
+ n1->u.dev.major = major;
+ n1->u.dev.minor = minor;
+ }
+ inode_dir_add(fs, n, name, n1);
+ inode_to_qid(qid, n1);
+ return 0;
+}
+
+static int fs_readlink(FSDevice *fs, char *buf, int buf_size, FSFile *f)
+{
+ FSINode *n = f->inode;
+ int len;
+ if (n->type != FT_LNK)
+ return -P9_EIO;
+ len = min_int(strlen(n->u.symlink.name), buf_size - 1);
+ memcpy(buf, n->u.symlink.name, len);
+ buf[len] = '\0';
+ return 0;
+}
+
+static int fs_renameat(FSDevice *fs, FSFile *f, const char *name,
+ FSFile *new_f, const char *new_name)
+{
+ FSDirEntry *de, *de1;
+ FSINode *n1;
+
+ de = inode_search(f->inode, name);
+ if (!de)
+ return -P9_ENOENT;
+ de1 = inode_search(new_f->inode, new_name);
+ n1 = NULL;
+ if (de1) {
+ n1 = de1->inode;
+ if (n1->type == FT_DIR)
+ return -P9_EEXIST; /* XXX: handle the case */
+ inode_dirent_delete_no_decref(fs, new_f->inode, de1);
+ }
+ inode_dir_add(fs, new_f->inode, new_name, inode_incref(fs, de->inode));
+ inode_dirent_delete(fs, f->inode, de);
+ if (n1)
+ inode_decref(fs, n1);
+ return 0;
+}
+
+static int fs_unlinkat(FSDevice *fs, FSFile *f, const char *name)
+{
+ FSDirEntry *de;
+ FSINode *n;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ return -P9_ENOENT;
+ de = inode_search(f->inode, name);
+ if (!de)
+ return -P9_ENOENT;
+ n = de->inode;
+ if (n->type == FT_DIR) {
+ if (!is_empty_dir(fs, n))
+ return -P9_ENOTEMPTY;
+ flush_dir(fs, n);
+ }
+ inode_dirent_delete(fs, f->inode, de);
+ return 0;
+}
+
+static int fs_lock(FSDevice *fs, FSFile *f, const FSLock *lock)
+{
+ FSINode *n = f->inode;
+ if (!f->is_opened)
+ return -P9_EPROTO;
+ if (n->type != FT_REG)
+ return -P9_EIO;
+ /* XXX: implement it */
+ return P9_LOCK_SUCCESS;
+}
+
+static int fs_getlock(FSDevice *fs, FSFile *f, FSLock *lock)
+{
+ FSINode *n = f->inode;
+ if (!f->is_opened)
+ return -P9_EPROTO;
+ if (n->type != FT_REG)
+ return -P9_EIO;
+ /* XXX: implement it */
+ return 0;
+}
+
+/* XXX: only used with file lists, so not all the data is released */
+static void fs_mem_end(FSDevice *fs1)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ struct list_head *el, *el1, *el2, *el3;
+ FSINode *n;
+ FSDirEntry *de;
+
+ list_for_each_safe(el, el1, &fs->inode_list) {
+ n = list_entry(el, FSINode, link);
+ n->refcount = 0;
+ if (n->type == FT_DIR) {
+ list_for_each_safe(el2, el3, &n->u.dir.de_list) {
+ de = list_entry(el2, FSDirEntry, link);
+ list_del(&de->link);
+ free(de);
+ }
+ init_list_head(&n->u.dir.de_list);
+ }
+ inode_free(fs1, n);
+ }
+ assert(list_empty(&fs->inode_cache_list));
+ free(fs->import_dir);
+}
+
+FSDevice *fs_mem_init(void)
+{
+ FSDeviceMem *fs;
+ FSDevice *fs1;
+ FSINode *n;
+
+ fs = mallocz(sizeof(*fs));
+ fs1 = &fs->common;
+
+ fs->common.fs_end = fs_mem_end;
+ fs->common.fs_delete = fs_delete;
+ fs->common.fs_statfs = fs_statfs;
+ fs->common.fs_attach = fs_attach;
+ fs->common.fs_walk = fs_walk;
+ fs->common.fs_mkdir = fs_mkdir;
+ fs->common.fs_open = fs_open;
+ fs->common.fs_create = fs_create;
+ fs->common.fs_stat = fs_stat;
+ fs->common.fs_setattr = fs_setattr;
+ fs->common.fs_close = fs_close;
+ fs->common.fs_readdir = fs_readdir;
+ fs->common.fs_read = fs_read;
+ fs->common.fs_write = fs_write;
+ fs->common.fs_link = fs_link;
+ fs->common.fs_symlink = fs_symlink;
+ fs->common.fs_mknod = fs_mknod;
+ fs->common.fs_readlink = fs_readlink;
+ fs->common.fs_renameat = fs_renameat;
+ fs->common.fs_unlinkat = fs_unlinkat;
+ fs->common.fs_lock = fs_lock;
+ fs->common.fs_getlock = fs_getlock;
+
+ init_list_head(&fs->inode_list);
+ fs->inode_num_alloc = 1;
+ fs->block_size_log2 = FS_BLOCK_SIZE_LOG2;
+ fs->block_size = 1 << fs->block_size_log2;
+ fs->inode_limit = 1 << 20; /* arbitrary */
+ fs->fs_max_blocks = 1 << (30 - fs->block_size_log2); /* arbitrary */
+
+ init_list_head(&fs->inode_cache_list);
+ fs->inode_cache_size_limit = DEFAULT_INODE_CACHE_SIZE;
+
+ init_list_head(&fs->preload_list);
+ init_list_head(&fs->preload_archive_list);
+
+ init_list_head(&fs->base_url_list);
+
+ /* create the root inode */
+ n = inode_new(fs1, FT_DIR, 0777, 0, 0);
+ inode_dir_add(fs1, n, ".", inode_incref(fs1, n));
+ inode_dir_add(fs1, n, "..", inode_incref(fs1, n));
+ fs->root_inode = n;
+
+ return (FSDevice *)fs;
+}
+
+static BOOL fs_is_net(FSDevice *fs)
+{
+ return (fs->fs_end == fs_mem_end);
+}
+
+static FSBaseURL *fs_find_base_url(FSDevice *fs1,
+ const char *base_url_id)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ struct list_head *el;
+ FSBaseURL *bu;
+
+ list_for_each(el, &fs->base_url_list) {
+ bu = list_entry(el, FSBaseURL, link);
+ if (!strcmp(bu->base_url_id, base_url_id))
+ return bu;
+ }
+ return NULL;
+}
+
+static void fs_base_url_decref(FSDevice *fs, FSBaseURL *bu)
+{
+ assert(bu->ref_count >= 1);
+ if (--bu->ref_count == 0) {
+ free(bu->base_url_id);
+ free(bu->url);
+ free(bu->user);
+ free(bu->password);
+ list_del(&bu->link);
+ free(bu);
+ }
+}
+
+static FSBaseURL *fs_net_set_base_url(FSDevice *fs1,
+ const char *base_url_id,
+ const char *url,
+ const char *user, const char *password,
+ AES_KEY *aes_state)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ FSBaseURL *bu;
+
+ assert(fs_is_net(fs1));
+ bu = fs_find_base_url(fs1, base_url_id);
+ if (!bu) {
+ bu = mallocz(sizeof(*bu));
+ bu->base_url_id = strdup(base_url_id);
+ bu->ref_count = 1;
+ list_add_tail(&bu->link, &fs->base_url_list);
+ } else {
+ free(bu->url);
+ free(bu->user);
+ free(bu->password);
+ }
+
+ bu->url = strdup(url);
+ if (user)
+ bu->user = strdup(user);
+ else
+ bu->user = NULL;
+ if (password)
+ bu->password = strdup(password);
+ else
+ bu->password = NULL;
+ if (aes_state) {
+ bu->encrypted = TRUE;
+ bu->aes_state = *aes_state;
+ } else {
+ bu->encrypted = FALSE;
+ }
+ return bu;
+}
+
+static int fs_net_reset_base_url(FSDevice *fs1,
+ const char *base_url_id)
+{
+ FSBaseURL *bu;
+
+ assert(fs_is_net(fs1));
+ bu = fs_find_base_url(fs1, base_url_id);
+ if (!bu)
+ return -P9_ENOENT;
+ fs_base_url_decref(fs1, bu);
+ return 0;
+}
+
+static void fs_net_set_fs_max_size(FSDevice *fs1, uint64_t fs_max_size)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+
+ assert(fs_is_net(fs1));
+ fs->fs_max_blocks = to_blocks(fs, fs_max_size);
+}
+
+static int fs_net_set_url(FSDevice *fs1, FSINode *n,
+ const char *base_url_id, FSFileID file_id, uint64_t size)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ FSBaseURL *bu;
+
+ assert(fs_is_net(fs1));
+
+ bu = fs_find_base_url(fs1, base_url_id);
+ if (!bu)
+ return -P9_ENOENT;
+
+ /* XXX: could accept more state */
+ if (n->type != FT_REG ||
+ n->u.reg.state != REG_STATE_LOCAL ||
+ n->u.reg.fbuf.allocated_size != 0)
+ return -P9_EIO;
+
+ if (size > 0) {
+ n->u.reg.state = REG_STATE_UNLOADED;
+ n->u.reg.base_url = bu;
+ bu->ref_count++;
+ n->u.reg.size = size;
+ fs->fs_blocks += to_blocks(fs, size);
+ n->u.reg.file_id = file_id;
+ }
+ return 0;
+}
+
+#ifdef DUMP_CACHE_LOAD
+
+#include "json.h"
+
+#define ARCHIVE_SIZE_MAX (4 << 20)
+
+static void fs_dump_add_file(struct list_head *head, const char *name)
+{
+ PreloadFile *pf;
+ pf = mallocz(sizeof(*pf));
+ pf->name = strdup(name);
+ list_add_tail(&pf->link, head);
+}
+
+static PreloadFile *fs_dump_find_file(struct list_head *head, const char *name)
+{
+ PreloadFile *pf;
+ struct list_head *el;
+ list_for_each(el, head) {
+ pf = list_entry(el, PreloadFile, link);
+ if (!strcmp(pf->name, name))
+ return pf;
+ }
+ return NULL;
+}
+
+static void dump_close_archive(FSDevice *fs1)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ if (fs->dump_archive_file) {
+ fclose(fs->dump_archive_file);
+ }
+ fs->dump_archive_file = NULL;
+ fs->dump_archive_size = 0;
+}
+
+static void dump_loaded_file(FSDevice *fs1, FSINode *n)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ char filename[1024];
+ const char *fname, *p;
+
+ if (!fs->dump_cache_load || !n->u.reg.filename)
+ return;
+ fname = n->u.reg.filename;
+
+ if (fs_dump_find_file(&fs->dump_preload_list, fname)) {
+ dump_close_archive(fs1);
+ p = strrchr(fname, '/');
+ if (!p)
+ p = fname;
+ else
+ p++;
+ free(fs->dump_archive_name);
+ fs->dump_archive_name = strdup(p);
+ fs->dump_started = TRUE;
+ fs->dump_archive_num = 0;
+
+ fprintf(fs->dump_preload_file, "\n%s :\n", fname);
+ }
+ if (!fs->dump_started)
+ return;
+
+ if (!fs->dump_archive_file) {
+ snprintf(filename, sizeof(filename), "%s/%s%d",
+ fs->dump_preload_dir, fs->dump_archive_name,
+ fs->dump_archive_num);
+ fs->dump_archive_file = fopen(filename, "wb");
+ if (!fs->dump_archive_file) {
+ perror(filename);
+ exit(1);
+ }
+ fprintf(fs->dump_preload_archive_file, "\n@.preload2/%s%d :\n",
+ fs->dump_archive_name, fs->dump_archive_num);
+ fprintf(fs->dump_preload_file, " @.preload2/%s%d\n",
+ fs->dump_archive_name, fs->dump_archive_num);
+ fflush(fs->dump_preload_file);
+ fs->dump_archive_num++;
+ }
+
+ if (n->u.reg.size >= ARCHIVE_SIZE_MAX) {
+ /* exclude large files from archive */
+ /* add indicative size */
+ fprintf(fs->dump_preload_file, " %s %" PRId64 "\n",
+ fname, n->u.reg.size);
+ fflush(fs->dump_preload_file);
+ } else {
+ fprintf(fs->dump_preload_archive_file, " %s %" PRId64 " %" PRIx64 "\n",
+ n->u.reg.filename, n->u.reg.size, n->u.reg.file_id);
+ fflush(fs->dump_preload_archive_file);
+ fwrite(n->u.reg.fbuf.data, 1, n->u.reg.size, fs->dump_archive_file);
+ fflush(fs->dump_archive_file);
+ fs->dump_archive_size += n->u.reg.size;
+ if (fs->dump_archive_size >= ARCHIVE_SIZE_MAX) {
+ dump_close_archive(fs1);
+ }
+ }
+}
+
+static JSONValue json_load(const char *filename)
+{
+ FILE *f;
+ JSONValue val;
+ size_t size;
+ char *buf;
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ perror(filename);
+ exit(1);
+ }
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ buf = malloc(size + 1);
+ fread(buf, 1, size, f);
+ fclose(f);
+ val = json_parse_value_len(buf, size);
+ free(buf);
+ return val;
+}
+
+void fs_dump_cache_load(FSDevice *fs1, const char *cfg_filename)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ JSONValue cfg, val, array;
+ char *fname;
+ const char *preload_dir, *name;
+ int i;
+
+ if (!fs_is_net(fs1))
+ return;
+ cfg = json_load(cfg_filename);
+ if (json_is_error(cfg)) {
+ fprintf(stderr, "%s\n", json_get_error(cfg));
+ exit(1);
+ }
+
+ val = json_object_get(cfg, "preload_dir");
+ if (json_is_undefined(cfg)) {
+ config_error:
+ exit(1);
+ }
+ preload_dir = json_get_str(val);
+ if (!preload_dir) {
+ fprintf(stderr, "expecting preload_filename\n");
+ goto config_error;
+ }
+ fs->dump_preload_dir = strdup(preload_dir);
+
+ init_list_head(&fs->dump_preload_list);
+ init_list_head(&fs->dump_exclude_list);
+
+ array = json_object_get(cfg, "preload");
+ if (array.type != JSON_ARRAY) {
+ fprintf(stderr, "expecting preload array\n");
+ goto config_error;
+ }
+ for(i = 0; i < array.u.array->len; i++) {
+ val = json_array_get(array, i);
+ name = json_get_str(val);
+ if (!name) {
+ fprintf(stderr, "expecting a string\n");
+ goto config_error;
+ }
+ fs_dump_add_file(&fs->dump_preload_list, name);
+ }
+ json_free(cfg);
+
+ fname = compose_path(fs->dump_preload_dir, "preload.txt");
+ fs->dump_preload_file = fopen(fname, "w");
+ if (!fs->dump_preload_file) {
+ perror(fname);
+ exit(1);
+ }
+ free(fname);
+
+ fname = compose_path(fs->dump_preload_dir, "preload_archive.txt");
+ fs->dump_preload_archive_file = fopen(fname, "w");
+ if (!fs->dump_preload_archive_file) {
+ perror(fname);
+ exit(1);
+ }
+ free(fname);
+
+ fs->dump_cache_load = TRUE;
+}
+#else
+void fs_dump_cache_load(FSDevice *fs1, const char *cfg_filename)
+{
+}
+#endif
+
+/***********************************************/
+/* file list processing */
+
+static int filelist_load_rec(FSDevice *fs1, const char **pp, FSINode *dir,
+ const char *path)
+{
+ // FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ char fname[1024], lname[1024];
+ int ret;
+ const char *p;
+ FSINodeTypeEnum type;
+ uint32_t mode, uid, gid;
+ uint64_t size;
+ FSINode *n;
+
+ p = *pp;
+ for(;;) {
+ /* skip comments or empty lines */
+ if (*p == '\0')
+ break;
+ if (*p == '#') {
+ skip_line(&p);
+ continue;
+ }
+ /* end of directory */
+ if (*p == '.') {
+ p++;
+ skip_line(&p);
+ break;
+ }
+ if (parse_uint32_base(&mode, &p, 8) < 0) {
+ fprintf(stderr, "invalid mode\n");
+ return -1;
+ }
+ type = mode >> 12;
+ mode &= 0xfff;
+
+ if (parse_uint32(&uid, &p) < 0) {
+ fprintf(stderr, "invalid uid\n");
+ return -1;
+ }
+
+ if (parse_uint32(&gid, &p) < 0) {
+ fprintf(stderr, "invalid gid\n");
+ return -1;
+ }
+
+ n = inode_new(fs1, type, mode, uid, gid);
+
+ size = 0;
+ switch(type) {
+ case FT_CHR:
+ case FT_BLK:
+ if (parse_uint32(&n->u.dev.major, &p) < 0) {
+ fprintf(stderr, "invalid major\n");
+ return -1;
+ }
+ if (parse_uint32(&n->u.dev.minor, &p) < 0) {
+ fprintf(stderr, "invalid minor\n");
+ return -1;
+ }
+ break;
+ case FT_REG:
+ if (parse_uint64(&size, &p) < 0) {
+ fprintf(stderr, "invalid size\n");
+ return -1;
+ }
+ break;
+ case FT_DIR:
+ inode_dir_add(fs1, n, ".", inode_incref(fs1, n));
+ inode_dir_add(fs1, n, "..", inode_incref(fs1, dir));
+ break;
+ default:
+ break;
+ }
+
+ /* modification time */
+ if (parse_time(&n->mtime_sec, &n->mtime_nsec, &p) < 0) {
+ fprintf(stderr, "invalid mtime\n");
+ return -1;
+ }
+
+ if (parse_fname(fname, sizeof(fname), &p) < 0) {
+ fprintf(stderr, "invalid filename\n");
+ return -1;
+ }
+ inode_dir_add(fs1, dir, fname, n);
+
+ if (type == FT_LNK) {
+ if (parse_fname(lname, sizeof(lname), &p) < 0) {
+ fprintf(stderr, "invalid symlink name\n");
+ return -1;
+ }
+ n->u.symlink.name = strdup(lname);
+ } else if (type == FT_REG && size > 0) {
+ FSFileID file_id;
+ if (parse_file_id(&file_id, &p) < 0) {
+ fprintf(stderr, "invalid file id\n");
+ return -1;
+ }
+ fs_net_set_url(fs1, n, "/", file_id, size);
+#ifdef DUMP_CACHE_LOAD
+ {
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ if (fs->dump_cache_load
+#ifdef DEBUG_CACHE
+ || 1
+#endif
+ ) {
+ n->u.reg.filename = compose_path(path, fname);
+ } else {
+ n->u.reg.filename = NULL;
+ }
+ }
+#endif
+ }
+
+ skip_line(&p);
+
+ if (type == FT_DIR) {
+ char *path1;
+ path1 = compose_path(path, fname);
+ ret = filelist_load_rec(fs1, &p, n, path1);
+ free(path1);
+ if (ret)
+ return ret;
+ }
+ }
+ *pp = p;
+ return 0;
+}
+
+static int filelist_load(FSDevice *fs1, const char *str)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ int ret;
+ const char *p;
+
+ if (parse_tag_version(str) != 1)
+ return -1;
+ p = skip_header(str);
+ if (!p)
+ return -1;
+ ret = filelist_load_rec(fs1, &p, fs->root_inode, "");
+ return ret;
+}
+
+/************************************************************/
+/* FS init from network */
+
+static void __attribute__((format(printf, 1, 2))) fatal_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "Error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+static void fs_create_cmd(FSDevice *fs)
+{
+ FSFile *root_fd;
+ FSQID qid;
+ FSINode *n;
+
+ assert(!fs->fs_attach(fs, &root_fd, &qid, 0, "", ""));
+ assert(!fs->fs_create(fs, &qid, root_fd, FSCMD_NAME, P9_O_RDWR | P9_O_TRUNC,
+ 0666, 0));
+ n = root_fd->inode;
+ n->u.reg.is_fscmd = TRUE;
+ fs->fs_delete(fs, root_fd);
+}
+
+typedef struct {
+ FSDevice *fs;
+ char *url;
+ void (*start_cb)(void *opaque);
+ void *start_opaque;
+
+ FSFile *root_fd;
+ FSFile *fd;
+ int file_index;
+
+} FSNetInitState;
+
+static void fs_initial_sync(FSDevice *fs,
+ const char *url, void (*start_cb)(void *opaque),
+ void *start_opaque);
+static void head_loaded(FSDevice *fs, FSFile *f, int64_t size, void *opaque);
+static void filelist_loaded(FSDevice *fs, FSFile *f, int64_t size, void *opaque);
+static void kernel_load_cb(FSDevice *fs, FSQID *qid, int err,
+ void *opaque);
+static int preload_parse(FSDevice *fs, const char *fname, BOOL is_new);
+
+#ifdef EMSCRIPTEN
+static FSDevice *fs_import_fs;
+#endif
+
+#define DEFAULT_IMPORT_FILE_PATH "/tmp"
+
+FSDevice *fs_net_init(const char *url, void (*start_cb)(void *opaque),
+ void *start_opaque)
+{
+ FSDevice *fs;
+ FSDeviceMem *fs1;
+
+ fs_wget_init();
+
+ fs = fs_mem_init();
+#ifdef EMSCRIPTEN
+ if (!fs_import_fs)
+ fs_import_fs = fs;
+#endif
+ fs1 = (FSDeviceMem *)fs;
+ fs1->import_dir = strdup(DEFAULT_IMPORT_FILE_PATH);
+
+ fs_create_cmd(fs);
+
+ if (url) {
+ fs_initial_sync(fs, url, start_cb, start_opaque);
+ }
+ return fs;
+}
+
+static void fs_initial_sync(FSDevice *fs,
+ const char *url, void (*start_cb)(void *opaque),
+ void *start_opaque)
+{
+ FSNetInitState *s;
+ FSFile *head_fd;
+ FSQID qid;
+ char *head_url;
+ char buf[128];
+ struct timeval tv;
+
+ s = mallocz(sizeof(*s));
+ s->fs = fs;
+ s->url = strdup(url);
+ s->start_cb = start_cb;
+ s->start_opaque = start_opaque;
+ assert(!fs->fs_attach(fs, &s->root_fd, &qid, 0, "", ""));
+
+ /* avoid using cached version */
+ gettimeofday(&tv, NULL);
+ snprintf(buf, sizeof(buf), HEAD_FILENAME "?nocache=%" PRId64,
+ (int64_t)tv.tv_sec * 1000000 + tv.tv_usec);
+ head_url = compose_url(s->url, buf);
+ head_fd = fs_dup(fs, s->root_fd);
+ assert(!fs->fs_create(fs, &qid, head_fd, ".head",
+ P9_O_RDWR | P9_O_TRUNC, 0644, 0));
+ fs_wget_file2(fs, head_fd, head_url, NULL, NULL, NULL, 0,
+ head_loaded, s, NULL);
+ free(head_url);
+}
+
+static void head_loaded(FSDevice *fs, FSFile *f, int64_t size, void *opaque)
+{
+ FSNetInitState *s = opaque;
+ char *buf, *root_url, *url;
+ char fname[FILEID_SIZE_MAX];
+ FSFileID root_id;
+ FSFile *new_filelist_fd;
+ FSQID qid;
+ uint64_t fs_max_size;
+
+ if (size < 0)
+ fatal_error("could not load 'head' file (HTTP error=%d)", -(int)size);
+
+ buf = malloc(size + 1);
+ fs->fs_read(fs, f, 0, (uint8_t *)buf, size);
+ buf[size] = '\0';
+ fs->fs_delete(fs, f);
+ fs->fs_unlinkat(fs, s->root_fd, ".head");
+
+ if (parse_tag_version(buf) != 1)
+ fatal_error("invalid head version");
+
+ if (parse_tag_file_id(&root_id, buf, "RootID") < 0)
+ fatal_error("expected RootID tag");
+
+ if (parse_tag_uint64(&fs_max_size, buf, "FSMaxSize") == 0 &&
+ fs_max_size >= ((uint64_t)1 << 20)) {
+ fs_net_set_fs_max_size(fs, fs_max_size);
+ }
+
+ /* set the Root URL in the filesystem */
+ root_url = compose_url(s->url, ROOT_FILENAME);
+ fs_net_set_base_url(fs, "/", root_url, NULL, NULL, NULL);
+
+ new_filelist_fd = fs_dup(fs, s->root_fd);
+ assert(!fs->fs_create(fs, &qid, new_filelist_fd, ".filelist.txt",
+ P9_O_RDWR | P9_O_TRUNC, 0644, 0));
+
+ file_id_to_filename(fname, root_id);
+ url = compose_url(root_url, fname);
+ fs_wget_file2(fs, new_filelist_fd, url, NULL, NULL, NULL, 0,
+ filelist_loaded, s, NULL);
+ free(root_url);
+ free(url);
+}
+
+static void filelist_loaded(FSDevice *fs, FSFile *f, int64_t size, void *opaque)
+{
+ FSNetInitState *s = opaque;
+ uint8_t *buf;
+
+ if (size < 0)
+ fatal_error("could not load file list (HTTP error=%d)", -(int)size);
+
+ buf = malloc(size + 1);
+ fs->fs_read(fs, f, 0, buf, size);
+ buf[size] = '\0';
+ fs->fs_delete(fs, f);
+ fs->fs_unlinkat(fs, s->root_fd, ".filelist.txt");
+
+ if (filelist_load(fs, (char *)buf) != 0)
+ fatal_error("error while parsing file list");
+
+ /* try to load the kernel and the preload file */
+ s->file_index = 0;
+ kernel_load_cb(fs, NULL, 0, s);
+}
+
+
+#define FILE_LOAD_COUNT 2
+
+static const char *kernel_file_list[FILE_LOAD_COUNT] = {
+ ".preload",
+ ".preload2/preload.txt",
+};
+
+static void kernel_load_cb(FSDevice *fs, FSQID *qid1, int err,
+ void *opaque)
+{
+ FSNetInitState *s = opaque;
+ FSQID qid;
+
+#ifdef DUMP_CACHE_LOAD
+ /* disable preloading if dumping cache load */
+ if (((FSDeviceMem *)fs)->dump_cache_load)
+ return;
+#endif
+
+ if (s->fd) {
+ fs->fs_delete(fs, s->fd);
+ s->fd = NULL;
+ }
+
+ if (s->file_index >= FILE_LOAD_COUNT) {
+ /* all files are loaded */
+ if (preload_parse(fs, ".preload2/preload.txt", TRUE) < 0) {
+ preload_parse(fs, ".preload", FALSE);
+ }
+ fs->fs_delete(fs, s->root_fd);
+ if (s->start_cb)
+ s->start_cb(s->start_opaque);
+ free(s);
+ } else {
+ s->fd = fs_walk_path(fs, s->root_fd, kernel_file_list[s->file_index++]);
+ if (!s->fd)
+ goto done;
+ err = fs->fs_open(fs, &qid, s->fd, P9_O_RDONLY, kernel_load_cb, s);
+ if (err <= 0) {
+ done:
+ kernel_load_cb(fs, NULL, 0, s);
+ }
+ }
+}
+
+static void preload_parse_str_old(FSDevice *fs1, const char *p)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ char fname[1024];
+ PreloadEntry *pe;
+ PreloadFile *pf;
+ FSINode *n;
+
+ for(;;) {
+ while (isspace_nolf(*p))
+ p++;
+ if (*p == '\n') {
+ p++;
+ continue;
+ }
+ if (*p == '\0')
+ break;
+ if (parse_fname(fname, sizeof(fname), &p) < 0) {
+ fprintf(stderr, "invalid filename\n");
+ return;
+ }
+ // printf("preload file='%s\n", fname);
+ n = inode_search_path(fs1, fname);
+ if (!n || n->type != FT_REG || n->u.reg.state == REG_STATE_LOCAL) {
+ fprintf(stderr, "invalid preload file: '%s'\n", fname);
+ while (*p != '\n' && *p != '\0')
+ p++;
+ } else {
+ pe = mallocz(sizeof(*pe));
+ pe->file_id = n->u.reg.file_id;
+ init_list_head(&pe->file_list);
+ list_add_tail(&pe->link, &fs->preload_list);
+ for(;;) {
+ while (isspace_nolf(*p))
+ p++;
+ if (*p == '\0' || *p == '\n')
+ break;
+ if (parse_fname(fname, sizeof(fname), &p) < 0) {
+ fprintf(stderr, "invalid filename\n");
+ return;
+ }
+ // printf(" adding '%s'\n", fname);
+ pf = mallocz(sizeof(*pf));
+ pf->name = strdup(fname);
+ list_add_tail(&pf->link, &pe->file_list);
+ }
+ }
+ }
+}
+
+static void preload_parse_str(FSDevice *fs1, const char *p)
+{
+ FSDeviceMem *fs = (FSDeviceMem *)fs1;
+ PreloadEntry *pe;
+ PreloadArchive *pa;
+ FSINode *n;
+ BOOL is_archive;
+ char fname[1024];
+
+ pe = NULL;
+ pa = NULL;
+ for(;;) {
+ while (isspace_nolf(*p))
+ p++;
+ if (*p == '\n') {
+ pe = NULL;
+ p++;
+ continue;
+ }
+ if (*p == '#')
+ continue; /* comment */
+ if (*p == '\0')
+ break;
+
+ is_archive = FALSE;
+ if (*p == '@') {
+ is_archive = TRUE;
+ p++;
+ }
+ if (parse_fname(fname, sizeof(fname), &p) < 0) {
+ fprintf(stderr, "invalid filename\n");
+ return;
+ }
+ while (isspace_nolf(*p))
+ p++;
+ if (*p == ':') {
+ p++;
+ // printf("preload file='%s' archive=%d\n", fname, is_archive);
+ n = inode_search_path(fs1, fname);
+ pe = NULL;
+ pa = NULL;
+ if (!n || n->type != FT_REG || n->u.reg.state == REG_STATE_LOCAL) {
+ fprintf(stderr, "invalid preload file: '%s'\n", fname);
+ while (*p != '\n' && *p != '\0')
+ p++;
+ } else if (is_archive) {
+ pa = mallocz(sizeof(*pa));
+ pa->name = strdup(fname);
+ init_list_head(&pa->file_list);
+ list_add_tail(&pa->link, &fs->preload_archive_list);
+ } else {
+ pe = mallocz(sizeof(*pe));
+ pe->file_id = n->u.reg.file_id;
+ init_list_head(&pe->file_list);
+ list_add_tail(&pe->link, &fs->preload_list);
+ }
+ } else {
+ if (!pe && !pa) {
+ fprintf(stderr, "filename without target: %s\n", fname);
+ return;
+ }
+ if (pa) {
+ PreloadArchiveFile *paf;
+ FSFileID file_id;
+ uint64_t size;
+
+ if (parse_uint64(&size, &p) < 0) {
+ fprintf(stderr, "invalid size\n");
+ return;
+ }
+
+ if (parse_file_id(&file_id, &p) < 0) {
+ fprintf(stderr, "invalid file id\n");
+ return;
+ }
+
+ paf = mallocz(sizeof(*paf));
+ paf->name = strdup(fname);
+ paf->file_id = file_id;
+ paf->size = size;
+ list_add_tail(&paf->link, &pa->file_list);
+ } else {
+ PreloadFile *pf;
+ pf = mallocz(sizeof(*pf));
+ pf->name = strdup(fname);
+ pf->is_archive = is_archive;
+ list_add_tail(&pf->link, &pe->file_list);
+ }
+ }
+ /* skip the rest of the line */
+ while (*p != '\n' && *p != '\0')
+ p++;
+ if (*p == '\n')
+ p++;
+ }
+}
+
+static int preload_parse(FSDevice *fs, const char *fname, BOOL is_new)
+{
+ FSINode *n;
+ char *buf;
+ size_t size;
+
+ n = inode_search_path(fs, fname);
+ if (!n || n->type != FT_REG || n->u.reg.state != REG_STATE_LOADED)
+ return -1;
+ /* transform to zero terminated string */
+ size = n->u.reg.size;
+ buf = malloc(size + 1);
+ file_buffer_read(&n->u.reg.fbuf, 0, (uint8_t *)buf, size);
+ buf[size] = '\0';
+ if (is_new)
+ preload_parse_str(fs, buf);
+ else
+ preload_parse_str_old(fs, buf);
+ free(buf);
+ return 0;
+}
+
+
+
+/************************************************************/
+/* FS user interface */
+
+typedef struct CmdXHRState {
+ FSFile *req_fd;
+ FSFile *root_fd;
+ FSFile *fd;
+ FSFile *post_fd;
+ AES_KEY aes_state;
+} CmdXHRState;
+
+static void fs_cmd_xhr_on_load(FSDevice *fs, FSFile *f, int64_t size,
+ void *opaque);
+
+static int parse_hex_buf(uint8_t *buf, int buf_size, const char **pp)
+{
+ char buf1[1024];
+ int len;
+
+ if (parse_fname(buf1, sizeof(buf1), pp) < 0)
+ return -1;
+ len = strlen(buf1);
+ if ((len & 1) != 0)
+ return -1;
+ len >>= 1;
+ if (len > buf_size)
+ return -1;
+ if (decode_hex(buf, buf1, len) < 0)
+ return -1;
+ return len;
+}
+
+static int fs_cmd_xhr(FSDevice *fs, FSFile *f,
+ const char *p, uint32_t uid, uint32_t gid)
+{
+ char url[1024], post_filename[1024], filename[1024];
+ char user_buf[128], *user;
+ char password_buf[128], *password;
+ FSQID qid;
+ FSFile *fd, *root_fd, *post_fd;
+ uint64_t post_data_len;
+ int err, aes_key_len;
+ CmdXHRState *s;
+ char *name;
+ AES_KEY *paes_state;
+ uint8_t aes_key[FS_KEY_LEN];
+ uint32_t flags;
+ FSCMDRequest *req;
+
+ /* a request is already done or in progress */
+ if (f->req != NULL)
+ return -P9_EIO;
+
+ if (parse_fname(url, sizeof(url), &p) < 0)
+ goto fail;
+ if (parse_fname(user_buf, sizeof(user_buf), &p) < 0)
+ goto fail;
+ if (parse_fname(password_buf, sizeof(password_buf), &p) < 0)
+ goto fail;
+ if (parse_fname(post_filename, sizeof(post_filename), &p) < 0)
+ goto fail;
+ if (parse_fname(filename, sizeof(filename), &p) < 0)
+ goto fail;
+ aes_key_len = parse_hex_buf(aes_key, FS_KEY_LEN, &p);
+ if (aes_key_len < 0)
+ goto fail;
+ if (parse_uint32(&flags, &p) < 0)
+ goto fail;
+ if (aes_key_len != 0 && aes_key_len != FS_KEY_LEN)
+ goto fail;
+
+ if (user_buf[0] != '\0')
+ user = user_buf;
+ else
+ user = NULL;
+ if (password_buf[0] != '\0')
+ password = password_buf;
+ else
+ password = NULL;
+
+ // printf("url='%s' '%s' '%s' filename='%s'\n", url, user, password, filename);
+ assert(!fs->fs_attach(fs, &root_fd, &qid, uid, "", ""));
+ post_fd = NULL;
+
+ fd = fs_walk_path1(fs, root_fd, filename, &name);
+ if (!fd) {
+ err = -P9_ENOENT;
+ goto fail1;
+ }
+ /* XXX: until fs_create is fixed */
+ fs->fs_unlinkat(fs, fd, name);
+
+ err = fs->fs_create(fs, &qid, fd, name,
+ P9_O_RDWR | P9_O_TRUNC, 0600, gid);
+ if (err < 0) {
+ goto fail1;
+ }
+
+ if (post_filename[0] != '\0') {
+ FSINode *n;
+
+ post_fd = fs_walk_path(fs, root_fd, post_filename);
+ if (!post_fd) {
+ err = -P9_ENOENT;
+ goto fail1;
+ }
+ err = fs->fs_open(fs, &qid, post_fd, P9_O_RDONLY, NULL, NULL);
+ if (err < 0)
+ goto fail1;
+ n = post_fd->inode;
+ assert(n->type == FT_REG && n->u.reg.state == REG_STATE_LOCAL);
+ post_data_len = n->u.reg.size;
+ } else {
+ post_data_len = 0;
+ }
+
+ s = mallocz(sizeof(*s));
+ s->root_fd = root_fd;
+ s->fd = fd;
+ s->post_fd = post_fd;
+ if (aes_key_len != 0) {
+ AES_set_decrypt_key(aes_key, FS_KEY_LEN * 8, &s->aes_state);
+ paes_state = &s->aes_state;
+ } else {
+ paes_state = NULL;
+ }
+
+ req = mallocz(sizeof(*req));
+ req->type = FS_CMD_XHR;
+ req->reply_len = 0;
+ req->xhr_state = s;
+ s->req_fd = f;
+ f->req = req;
+
+ fs_wget_file2(fs, fd, url, user, password, post_fd, post_data_len,
+ fs_cmd_xhr_on_load, s, paes_state);
+ return 0;
+ fail1:
+ if (fd)
+ fs->fs_delete(fs, fd);
+ if (post_fd)
+ fs->fs_delete(fs, post_fd);
+ fs->fs_delete(fs, root_fd);
+ return err;
+ fail:
+ return -P9_EIO;
+}
+
+static void fs_cmd_xhr_on_load(FSDevice *fs, FSFile *f, int64_t size,
+ void *opaque)
+{
+ CmdXHRState *s = opaque;
+ FSCMDRequest *req;
+ int ret;
+
+ // printf("fs_cmd_xhr_on_load: size=%d\n", (int)size);
+
+ if (s->fd)
+ fs->fs_delete(fs, s->fd);
+ if (s->post_fd)
+ fs->fs_delete(fs, s->post_fd);
+ fs->fs_delete(fs, s->root_fd);
+
+ if (s->req_fd) {
+ req = s->req_fd->req;
+ if (size < 0) {
+ ret = size;
+ } else {
+ ret = 0;
+ }
+ put_le32(req->reply_buf, ret);
+ req->reply_len = sizeof(ret);
+ req->xhr_state = NULL;
+ }
+ free(s);
+}
+
+static int fs_cmd_set_base_url(FSDevice *fs, const char *p)
+{
+ // FSDeviceMem *fs1 = (FSDeviceMem *)fs;
+ char url[1024], base_url_id[1024];
+ char user_buf[128], *user;
+ char password_buf[128], *password;
+ AES_KEY aes_state, *paes_state;
+ uint8_t aes_key[FS_KEY_LEN];
+ int aes_key_len;
+
+ if (parse_fname(base_url_id, sizeof(base_url_id), &p) < 0)
+ goto fail;
+ if (parse_fname(url, sizeof(url), &p) < 0)
+ goto fail;
+ if (parse_fname(user_buf, sizeof(user_buf), &p) < 0)
+ goto fail;
+ if (parse_fname(password_buf, sizeof(password_buf), &p) < 0)
+ goto fail;
+ aes_key_len = parse_hex_buf(aes_key, FS_KEY_LEN, &p);
+ if (aes_key_len < 0)
+ goto fail;
+
+ if (user_buf[0] != '\0')
+ user = user_buf;
+ else
+ user = NULL;
+ if (password_buf[0] != '\0')
+ password = password_buf;
+ else
+ password = NULL;
+
+ if (aes_key_len != 0) {
+ if (aes_key_len != FS_KEY_LEN)
+ goto fail;
+ AES_set_decrypt_key(aes_key, FS_KEY_LEN * 8, &aes_state);
+ paes_state = &aes_state;
+ } else {
+ paes_state = NULL;
+ }
+
+ fs_net_set_base_url(fs, base_url_id, url, user, password,
+ paes_state);
+ return 0;
+ fail:
+ return -P9_EINVAL;
+}
+
+static int fs_cmd_reset_base_url(FSDevice *fs, const char *p)
+{
+ char base_url_id[1024];
+
+ if (parse_fname(base_url_id, sizeof(base_url_id), &p) < 0)
+ goto fail;
+ fs_net_reset_base_url(fs, base_url_id);
+ return 0;
+ fail:
+ return -P9_EINVAL;
+}
+
+static int fs_cmd_set_url(FSDevice *fs, const char *p)
+{
+ char base_url_id[1024];
+ char filename[1024];
+ FSFileID file_id;
+ uint64_t size;
+ FSINode *n;
+
+ if (parse_fname(filename, sizeof(filename), &p) < 0)
+ goto fail;
+ if (parse_fname(base_url_id, sizeof(base_url_id), &p) < 0)
+ goto fail;
+ if (parse_file_id(&file_id, &p) < 0)
+ goto fail;
+ if (parse_uint64(&size, &p) < 0)
+ goto fail;
+
+ n = inode_search_path(fs, filename);
+ if (!n) {
+ return -P9_ENOENT;
+ }
+ return fs_net_set_url(fs, n, base_url_id, file_id, size);
+ fail:
+ return -P9_EINVAL;
+}
+
+static int fs_cmd_export_file(FSDevice *fs, const char *p)
+{
+ char filename[1024];
+ FSINode *n;
+ const char *name;
+ uint8_t *buf;
+
+ if (parse_fname(filename, sizeof(filename), &p) < 0)
+ goto fail;
+ n = inode_search_path(fs, filename);
+ if (!n)
+ return -P9_ENOENT;
+ if (n->type != FT_REG ||
+ (n->u.reg.state != REG_STATE_LOCAL &&
+ n->u.reg.state != REG_STATE_LOADED))
+ goto fail;
+ name = strrchr(filename, '/');
+ if (name)
+ name++;
+ else
+ name = filename;
+ /* XXX: pass the buffer to JS to avoid the allocation */
+ buf = malloc(n->u.reg.size);
+ file_buffer_read(&n->u.reg.fbuf, 0, buf, n->u.reg.size);
+ fs_export_file(name, buf, n->u.reg.size);
+ free(buf);
+ return 0;
+ fail:
+ return -P9_EIO;
+}
+
+/* PBKDF2 crypto acceleration */
+static int fs_cmd_pbkdf2(FSDevice *fs, FSFile *f, const char *p)
+{
+ uint8_t pwd[1024];
+ uint8_t salt[128];
+ uint32_t iter, key_len;
+ int pwd_len, salt_len;
+ FSCMDRequest *req;
+
+ /* a request is already done or in progress */
+ if (f->req != NULL)
+ return -P9_EIO;
+
+ pwd_len = parse_hex_buf(pwd, sizeof(pwd), &p);
+ if (pwd_len < 0)
+ goto fail;
+ salt_len = parse_hex_buf(salt, sizeof(salt), &p);
+ if (pwd_len < 0)
+ goto fail;
+ if (parse_uint32(&iter, &p) < 0)
+ goto fail;
+ if (parse_uint32(&key_len, &p) < 0)
+ goto fail;
+ if (key_len > FS_CMD_REPLY_LEN_MAX ||
+ key_len == 0)
+ goto fail;
+ req = mallocz(sizeof(*req));
+ req->type = FS_CMD_PBKDF2;
+ req->reply_len = key_len;
+ pbkdf2_hmac_sha256(pwd, pwd_len, salt, salt_len, iter, key_len,
+ req->reply_buf);
+ f->req = req;
+ return 0;
+ fail:
+ return -P9_EINVAL;
+}
+
+static int fs_cmd_set_import_dir(FSDevice *fs, FSFile *f, const char *p)
+{
+ FSDeviceMem *fs1 = (FSDeviceMem *)fs;
+ char filename[1024];
+
+ if (parse_fname(filename, sizeof(filename), &p) < 0)
+ return -P9_EINVAL;
+ free(fs1->import_dir);
+ fs1->import_dir = strdup(filename);
+ return 0;
+}
+
+static int fs_cmd_write(FSDevice *fs, FSFile *f, uint64_t offset,
+ const uint8_t *buf, int buf_len)
+{
+ char *buf1;
+ const char *p;
+ char cmd[64];
+ int err;
+
+ /* transform into a string */
+ buf1 = malloc(buf_len + 1);
+ memcpy(buf1, buf, buf_len);
+ buf1[buf_len] = '\0';
+
+ err = 0;
+ p = buf1;
+ if (parse_fname(cmd, sizeof(cmd), &p) < 0)
+ goto fail;
+ if (!strcmp(cmd, "xhr")) {
+ err = fs_cmd_xhr(fs, f, p, f->uid, 0);
+ } else if (!strcmp(cmd, "set_base_url")) {
+ err = fs_cmd_set_base_url(fs, p);
+ } else if (!strcmp(cmd, "reset_base_url")) {
+ err = fs_cmd_reset_base_url(fs, p);
+ } else if (!strcmp(cmd, "set_url")) {
+ err = fs_cmd_set_url(fs, p);
+ } else if (!strcmp(cmd, "export_file")) {
+ err = fs_cmd_export_file(fs, p);
+ } else if (!strcmp(cmd, "pbkdf2")) {
+ err = fs_cmd_pbkdf2(fs, f, p);
+ } else if (!strcmp(cmd, "set_import_dir")) {
+ err = fs_cmd_set_import_dir(fs, f, p);
+ } else {
+ printf("unknown command: '%s'\n", cmd);
+ fail:
+ err = -P9_EIO;
+ }
+ free(buf1);
+ if (err == 0)
+ return buf_len;
+ else
+ return err;
+}
+
+static int fs_cmd_read(FSDevice *fs, FSFile *f, uint64_t offset,
+ uint8_t *buf, int buf_len)
+{
+ FSCMDRequest *req;
+ int l;
+
+ req = f->req;
+ if (!req)
+ return -P9_EIO;
+ l = min_int(req->reply_len, buf_len);
+ memcpy(buf, req->reply_buf, l);
+ return l;
+}
+
+static void fs_cmd_close(FSDevice *fs, FSFile *f)
+{
+ FSCMDRequest *req;
+ req = f->req;
+
+ if (req) {
+ if (req->xhr_state) {
+ req->xhr_state->req_fd = NULL;
+ }
+ free(req);
+ f->req = NULL;
+ }
+}
+
+/* Create a .fscmd_pwd file to avoid passing the password thru the
+ Linux command line */
+void fs_net_set_pwd(FSDevice *fs, const char *pwd)
+{
+ FSFile *root_fd;
+ FSQID qid;
+
+ assert(fs_is_net(fs));
+
+ assert(!fs->fs_attach(fs, &root_fd, &qid, 0, "", ""));
+ assert(!fs->fs_create(fs, &qid, root_fd, ".fscmd_pwd", P9_O_RDWR | P9_O_TRUNC,
+ 0600, 0));
+ fs->fs_write(fs, root_fd, 0, (uint8_t *)pwd, strlen(pwd));
+ fs->fs_delete(fs, root_fd);
+}
+
+/* external file import */
+
+#ifdef EMSCRIPTEN
+
+void fs_import_file(const char *filename, uint8_t *buf, int buf_len)
+{
+ FSDevice *fs;
+ FSDeviceMem *fs1;
+ FSFile *fd, *root_fd;
+ FSQID qid;
+
+ // printf("importing file: %s len=%d\n", filename, buf_len);
+ fs = fs_import_fs;
+ if (!fs) {
+ free(buf);
+ return;
+ }
+
+ assert(!fs->fs_attach(fs, &root_fd, &qid, 1000, "", ""));
+ fs1 = (FSDeviceMem *)fs;
+ fd = fs_walk_path(fs, root_fd, fs1->import_dir);
+ if (!fd)
+ goto fail;
+ fs_unlinkat(fs, root_fd, filename);
+ if (fs->fs_create(fs, &qid, fd, filename, P9_O_RDWR | P9_O_TRUNC,
+ 0600, 0) < 0)
+ goto fail;
+ fs->fs_write(fs, fd, 0, buf, buf_len);
+ fail:
+ if (fd)
+ fs->fs_delete(fs, fd);
+ if (root_fd)
+ fs->fs_delete(fs, root_fd);
+ free(buf);
+}
+
+#else
+
+void fs_export_file(const char *filename,
+ const uint8_t *buf, int buf_len)
+{
+}
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs_utils.c b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_utils.c
new file mode 100644
index 0000000..37f399d
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_utils.c
@@ -0,0 +1,370 @@
+/*
+ * Misc FS utilities
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/file.h>
+
+#include "cutils.h"
+#include "list.h"
+#include "fs_utils.h"
+
+/* last byte is the version */
+const uint8_t encrypted_file_magic[4] = { 0xfb, 0xa2, 0xe9, 0x01 };
+
+char *compose_path(const char *path, const char *name)
+{
+ int path_len, name_len;
+ char *d, *q;
+
+ if (path[0] == '\0') {
+ d = strdup(name);
+ } else {
+ path_len = strlen(path);
+ name_len = strlen(name);
+ d = malloc(path_len + 1 + name_len + 1);
+ q = d;
+ memcpy(q, path, path_len);
+ q += path_len;
+ if (path[path_len - 1] != '/')
+ *q++ = '/';
+ memcpy(q, name, name_len + 1);
+ }
+ return d;
+}
+
+char *compose_url(const char *base_url, const char *name)
+{
+ if (strchr(name, ':')) {
+ return strdup(name);
+ } else {
+ return compose_path(base_url, name);
+ }
+}
+
+void skip_line(const char **pp)
+{
+ const char *p;
+ p = *pp;
+ while (*p != '\n' && *p != '\0')
+ p++;
+ if (*p == '\n')
+ p++;
+ *pp = p;
+}
+
+char *quoted_str(const char *str)
+{
+ const char *s;
+ char *q;
+ int c;
+ char *buf;
+
+ if (str[0] == '\0')
+ goto use_quote;
+ s = str;
+ while (*s != '\0') {
+ if (*s <= ' ' || *s > '~')
+ goto use_quote;
+ s++;
+ }
+ return strdup(str);
+ use_quote:
+ buf = malloc(strlen(str) * 4 + 2 + 1);
+ q = buf;
+ s = str;
+ *q++ = '"';
+ while (*s != '\0') {
+ c = *(uint8_t *)s;
+ if (c < ' ' || c == 127) {
+ q += sprintf(q, "\\x%02x", c);
+ } else if (c == '\\' || c == '\"') {
+ q += sprintf(q, "\\%c", c);
+ } else {
+ *q++ = c;
+ }
+ s++;
+ }
+ *q++ = '"';
+ *q = '\0';
+ return buf;
+}
+
+int parse_fname(char *buf, int buf_size, const char **pp)
+{
+ const char *p;
+ char *q;
+ int c, h;
+
+ p = *pp;
+ while (isspace_nolf(*p))
+ p++;
+ if (*p == '\0')
+ return -1;
+ q = buf;
+ if (*p == '"') {
+ p++;
+ for(;;) {
+ c = *p++;
+ if (c == '\0' || c == '\n') {
+ return -1;
+ } else if (c == '\"') {
+ break;
+ } else if (c == '\\') {
+ c = *p++;
+ switch(c) {
+ case '\'':
+ case '\"':
+ case '\\':
+ goto add_char;
+ case 'n':
+ c = '\n';
+ goto add_char;
+ case 'r':
+ c = '\r';
+ goto add_char;
+ case 't':
+ c = '\t';
+ goto add_char;
+ case 'x':
+ h = from_hex(*p++);
+ if (h < 0)
+ return -1;
+ c = h << 4;
+ h = from_hex(*p++);
+ if (h < 0)
+ return -1;
+ c |= h;
+ goto add_char;
+ default:
+ return -1;
+ }
+ } else {
+ add_char:
+ if (q >= buf + buf_size - 1)
+ return -1;
+ *q++ = c;
+ }
+ }
+ } else {
+ while (!isspace_nolf(*p) && *p != '\0' && *p != '\n') {
+ if (q >= buf + buf_size - 1)
+ return -1;
+ *q++ = *p++;
+ }
+ }
+ *q = '\0';
+ *pp = p;
+ return 0;
+}
+
+int parse_uint32_base(uint32_t *pval, const char **pp, int base)
+{
+ const char *p, *p1;
+ p = *pp;
+ while (isspace_nolf(*p))
+ p++;
+ *pval = strtoul(p, (char **)&p1, base);
+ if (p1 == p)
+ return -1;
+ *pp = p1;
+ return 0;
+}
+
+int parse_uint64_base(uint64_t *pval, const char **pp, int base)
+{
+ const char *p, *p1;
+ p = *pp;
+ while (isspace_nolf(*p))
+ p++;
+ *pval = strtoull(p, (char **)&p1, base);
+ if (p1 == p)
+ return -1;
+ *pp = p1;
+ return 0;
+}
+
+int parse_uint64(uint64_t *pval, const char **pp)
+{
+ return parse_uint64_base(pval, pp, 0);
+}
+
+int parse_uint32(uint32_t *pval, const char **pp)
+{
+ return parse_uint32_base(pval, pp, 0);
+}
+
+int parse_time(uint32_t *psec, uint32_t *pnsec, const char **pp)
+{
+ const char *p;
+ uint32_t v, m;
+ p = *pp;
+ if (parse_uint32(psec, &p) < 0)
+ return -1;
+ v = 0;
+ if (*p == '.') {
+ p++;
+ /* XXX: inefficient */
+ m = 1000000000;
+ v = 0;
+ while (*p >= '0' && *p <= '9') {
+ m /= 10;
+ v += (*p - '0') * m;
+ p++;
+ }
+ }
+ *pnsec = v;
+ *pp = p;
+ return 0;
+}
+
+int parse_file_id(FSFileID *pval, const char **pp)
+{
+ return parse_uint64_base(pval, pp, 16);
+}
+
+char *file_id_to_filename(char *buf, FSFileID file_id)
+{
+ sprintf(buf, "%016" PRIx64, file_id);
+ return buf;
+}
+
+void encode_hex(char *str, const uint8_t *buf, int len)
+{
+ int i;
+ for(i = 0; i < len; i++)
+ sprintf(str + 2 * i, "%02x", buf[i]);
+}
+
+int decode_hex(uint8_t *buf, const char *str, int len)
+{
+ int h0, h1, i;
+
+ for(i = 0; i < len; i++) {
+ h0 = from_hex(str[2 * i]);
+ if (h0 < 0)
+ return -1;
+ h1 = from_hex(str[2 * i + 1]);
+ if (h1 < 0)
+ return -1;
+ buf[i] = (h0 << 4) | h1;
+ }
+ return 0;
+}
+
+/* return NULL if no end of header found */
+const char *skip_header(const char *p)
+{
+ p = strstr(p, "\n\n");
+ if (!p)
+ return NULL;
+ return p + 2;
+}
+
+/* return 0 if OK, < 0 if error */
+int parse_tag(char *buf, int buf_size, const char *str, const char *tag)
+{
+ char tagname[128], *q;
+ const char *p, *p1;
+ int len;
+
+ p = str;
+ for(;;) {
+ if (*p == '\0' || *p == '\n')
+ break;
+ q = tagname;
+ while (*p != ':' && *p != '\n' && *p != '\0') {
+ if ((q - tagname) < sizeof(tagname) - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ if (*p != ':')
+ return -1;
+ p++;
+ while (isspace_nolf(*p))
+ p++;
+ p1 = p;
+ p = strchr(p, '\n');
+ if (!p)
+ len = strlen(p1);
+ else
+ len = p - p1;
+ if (!strcmp(tagname, tag)) {
+ if (len > buf_size - 1)
+ len = buf_size - 1;
+ memcpy(buf, p1, len);
+ buf[len] = '\0';
+ return 0;
+ }
+ if (!p)
+ break;
+ else
+ p++;
+ }
+ return -1;
+}
+
+int parse_tag_uint64(uint64_t *pval, const char *str, const char *tag)
+{
+ char buf[64];
+ const char *p;
+ if (parse_tag(buf, sizeof(buf), str, tag))
+ return -1;
+ p = buf;
+ return parse_uint64(pval, &p);
+}
+
+int parse_tag_file_id(FSFileID *pval, const char *str, const char *tag)
+{
+ char buf[64];
+ const char *p;
+ if (parse_tag(buf, sizeof(buf), str, tag))
+ return -1;
+ p = buf;
+ return parse_uint64_base(pval, &p, 16);
+}
+
+int parse_tag_version(const char *str)
+{
+ uint64_t version;
+ if (parse_tag_uint64(&version, str, "Version"))
+ return -1;
+ return version;
+}
+
+BOOL is_url(const char *path)
+{
+ return (strstart(path, "http:", NULL) ||
+ strstart(path, "https:", NULL) ||
+ strstart(path, "file:", NULL));
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs_utils.h b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_utils.h
new file mode 100644
index 0000000..4e4b9cd
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_utils.h
@@ -0,0 +1,95 @@
+/*
+ * Misc FS utilities
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define HEAD_FILENAME "head"
+#define ROOT_FILENAME "files"
+
+#define FILEID_SIZE_MAX 32
+
+#define FS_KEY_LEN 16
+
+/* default block size to determine the total filesytem size */
+#define FS_BLOCK_SIZE_LOG2 12
+#define FS_BLOCK_SIZE (1 << FS_BLOCK_SIZE_LOG2)
+
+typedef enum {
+ FS_ERR_OK = 0,
+ FS_ERR_GENERIC = -1,
+ FS_ERR_SYNTAX = -2,
+ FS_ERR_REVISION = -3,
+ FS_ERR_FILE_ID = -4,
+ FS_ERR_IO = -5,
+ FS_ERR_NOENT = -6,
+ FS_ERR_COUNTERS = -7,
+ FS_ERR_QUOTA = -8,
+ FS_ERR_PROTOCOL_VERSION = -9,
+ FS_ERR_HEAD = -10,
+} FSCommitErrorCode;
+
+typedef uint64_t FSFileID;
+
+static inline BOOL isspace_nolf(int c)
+{
+ return (c == ' ' || c == '\t');
+}
+
+static inline int from_hex(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else
+ return -1;
+}
+
+static inline uint64_t block_align(uint64_t val, uint64_t align)
+{
+ return (val + align - 1) & ~(align - 1);
+}
+
+void pstrcpy(char *buf, int buf_size, const char *str);
+char *pstrcat(char *buf, int buf_size, const char *s);
+char *compose_path(const char *path, const char *name);
+char *compose_url(const char *base_url, const char *name);
+void skip_line(const char **pp);
+char *quoted_str(const char *str);
+int parse_fname(char *buf, int buf_size, const char **pp);
+int parse_uint32_base(uint32_t *pval, const char **pp, int base);
+int parse_uint64_base(uint64_t *pval, const char **pp, int base);
+int parse_uint64(uint64_t *pval, const char **pp);
+int parse_uint32(uint32_t *pval, const char **pp);
+int parse_time(uint32_t *psec, uint32_t *pnsec, const char **pp);
+int parse_file_id(FSFileID *pval, const char **pp);
+char *file_id_to_filename(char *buf, FSFileID file_id);
+void encode_hex(char *str, const uint8_t *buf, int len);
+int decode_hex(uint8_t *buf, const char *str, int len);
+BOOL is_url(const char *path);
+
+const char *skip_header(const char *p);
+int parse_tag(char *buf, int buf_size, const char *str, const char *tag);
+int parse_tag_uint64(uint64_t *pval, const char *str, const char *tag);
+int parse_tag_file_id(FSFileID *pval, const char *str, const char *tag);
+int parse_tag_version(const char *str);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c
new file mode 100644
index 0000000..b4857b0
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c
@@ -0,0 +1,625 @@
+/*
+ * HTTP file download
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <ctype.h>
+
+#include "cutils.h"
+#include "list.h"
+#include "fs.h"
+#include "fs_utils.h"
+#include "fs_wget.h"
+
+#if defined(EMSCRIPTEN)
+#include <emscripten.h>
+#else
+#include <curl/multi.h>
+#endif
+
+/***********************************************/
+/* HTTP get */
+
+#ifdef EMSCRIPTEN
+
+struct XHRState {
+ void *opaque;
+ WGetWriteCallback *cb;
+};
+
+static int downloading_count;
+
+void fs_wget_init(void)
+{
+}
+
+extern void fs_wget_update_downloading(int flag);
+
+static void fs_wget_update_downloading_count(int incr)
+{
+ int prev_state, state;
+ prev_state = (downloading_count > 0);
+ downloading_count += incr;
+ state = (downloading_count > 0);
+ if (prev_state != state)
+ fs_wget_update_downloading(state);
+}
+
+static void fs_wget_onerror(unsigned int handle, void *opaque, int status,
+ const char *status_text)
+{
+ XHRState *s = opaque;
+ if (status <= 0)
+ status = -404; /* HTTP not found error */
+ else
+ status = -status;
+ fs_wget_update_downloading_count(-1);
+ if (s->cb)
+ s->cb(s->opaque, status, NULL, 0);
+}
+
+static void fs_wget_onload(unsigned int handle,
+ void *opaque, void *data, unsigned int size)
+{
+ XHRState *s = opaque;
+ fs_wget_update_downloading_count(-1);
+ if (s->cb)
+ s->cb(s->opaque, 0, data, size);
+}
+
+extern int emscripten_async_wget3_data(const char* url, const char* requesttype, const char *user, const char *password, const uint8_t *post_data, int post_data_len, void *arg, int free, em_async_wget2_data_onload_func onload, em_async_wget2_data_onerror_func onerror, em_async_wget2_data_onprogress_func onprogress);
+
+XHRState *fs_wget2(const char *url, const char *user, const char *password,
+ WGetReadCallback *read_cb, uint64_t post_data_len,
+ void *opaque, WGetWriteCallback *cb, BOOL single_write)
+{
+ XHRState *s;
+ const char *request;
+ uint8_t *post_data;
+
+ s = mallocz(sizeof(*s));
+ s->opaque = opaque;
+ s->cb = cb;
+
+ if (post_data_len != 0) {
+ request = "POST";
+ post_data = malloc(post_data_len);
+ read_cb(opaque, post_data, post_data_len);
+ } else {
+ request = "GET";
+ post_data = NULL;
+ }
+ fs_wget_update_downloading_count(1);
+
+ emscripten_async_wget3_data(url, request, user, password,
+ post_data, post_data_len, s, 1, fs_wget_onload,
+ fs_wget_onerror, NULL);
+ if (post_data_len != 0)
+ free(post_data);
+ return s;
+}
+
+void fs_wget_free(XHRState *s)
+{
+ s->cb = NULL;
+ s->opaque = NULL;
+}
+
+#else
+
+struct XHRState {
+ struct list_head link;
+ CURL *eh;
+ void *opaque;
+ WGetWriteCallback *write_cb;
+ WGetReadCallback *read_cb;
+
+ BOOL single_write;
+ DynBuf dbuf; /* used if single_write */
+};
+
+typedef struct {
+ struct list_head link;
+ int64_t timeout;
+ void (*cb)(void *opaque);
+ void *opaque;
+} AsyncCallState;
+
+static CURLM *curl_multi_ctx;
+static struct list_head xhr_list; /* list of XHRState.link */
+
+void fs_wget_init(void)
+{
+ if (curl_multi_ctx)
+ return;
+ curl_global_init(CURL_GLOBAL_ALL);
+ curl_multi_ctx = curl_multi_init();
+ init_list_head(&xhr_list);
+}
+
+void fs_wget_end(void)
+{
+ curl_multi_cleanup(curl_multi_ctx);
+ curl_global_cleanup();
+}
+
+static size_t fs_wget_write_cb(char *ptr, size_t size, size_t nmemb,
+ void *userdata)
+{
+ XHRState *s = userdata;
+ size *= nmemb;
+
+ if (s->single_write) {
+ dbuf_write(&s->dbuf, s->dbuf.size, (void *)ptr, size);
+ } else {
+ s->write_cb(s->opaque, 1, ptr, size);
+ }
+ return size;
+}
+
+static size_t fs_wget_read_cb(char *ptr, size_t size, size_t nmemb,
+ void *userdata)
+{
+ XHRState *s = userdata;
+ size *= nmemb;
+ return s->read_cb(s->opaque, ptr, size);
+}
+
+XHRState *fs_wget2(const char *url, const char *user, const char *password,
+ WGetReadCallback *read_cb, uint64_t post_data_len,
+ void *opaque, WGetWriteCallback *write_cb, BOOL single_write)
+{
+ XHRState *s;
+ s = mallocz(sizeof(*s));
+ s->eh = curl_easy_init();
+ s->opaque = opaque;
+ s->write_cb = write_cb;
+ s->read_cb = read_cb;
+ s->single_write = single_write;
+ dbuf_init(&s->dbuf);
+
+ curl_easy_setopt(s->eh, CURLOPT_PRIVATE, s);
+ curl_easy_setopt(s->eh, CURLOPT_WRITEDATA, s);
+ curl_easy_setopt(s->eh, CURLOPT_WRITEFUNCTION, fs_wget_write_cb);
+ curl_easy_setopt(s->eh, CURLOPT_HEADER, 0);
+ curl_easy_setopt(s->eh, CURLOPT_URL, url);
+ curl_easy_setopt(s->eh, CURLOPT_VERBOSE, 0L);
+ curl_easy_setopt(s->eh, CURLOPT_ACCEPT_ENCODING, "");
+ if (user) {
+ curl_easy_setopt(s->eh, CURLOPT_USERNAME, user);
+ curl_easy_setopt(s->eh, CURLOPT_PASSWORD, password);
+ }
+ if (post_data_len != 0) {
+ struct curl_slist *headers = NULL;
+ headers = curl_slist_append(headers,
+ "Content-Type: application/octet-stream");
+ curl_easy_setopt(s->eh, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(s->eh, CURLOPT_POST, 1L);
+ curl_easy_setopt(s->eh, CURLOPT_POSTFIELDSIZE_LARGE,
+ (curl_off_t)post_data_len);
+ curl_easy_setopt(s->eh, CURLOPT_READDATA, s);
+ curl_easy_setopt(s->eh, CURLOPT_READFUNCTION, fs_wget_read_cb);
+ }
+ curl_multi_add_handle(curl_multi_ctx, s->eh);
+ list_add_tail(&s->link, &xhr_list);
+ return s;
+}
+
+void fs_wget_free(XHRState *s)
+{
+ dbuf_free(&s->dbuf);
+ curl_easy_cleanup(s->eh);
+ list_del(&s->link);
+ free(s);
+}
+
+/* timeout is in ms */
+void fs_net_set_fdset(int *pfd_max, fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int *ptimeout)
+{
+ long timeout;
+ int n, fd_max;
+ CURLMsg *msg;
+
+ if (!curl_multi_ctx)
+ return;
+
+ curl_multi_perform(curl_multi_ctx, &n);
+
+ for(;;) {
+ msg = curl_multi_info_read(curl_multi_ctx, &n);
+ if (!msg)
+ break;
+ if (msg->msg == CURLMSG_DONE) {
+ XHRState *s;
+ long http_code;
+
+ curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **)&s);
+ curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE,
+ &http_code);
+ /* signal the end of the transfer or error */
+ if (http_code == 200) {
+ if (s->single_write) {
+ s->write_cb(s->opaque, 0, s->dbuf.buf, s->dbuf.size);
+ } else {
+ s->write_cb(s->opaque, 0, NULL, 0);
+ }
+ } else {
+ s->write_cb(s->opaque, -http_code, NULL, 0);
+ }
+ curl_multi_remove_handle(curl_multi_ctx, s->eh);
+ curl_easy_cleanup(s->eh);
+ dbuf_free(&s->dbuf);
+ list_del(&s->link);
+ free(s);
+ }
+ }
+
+ curl_multi_fdset(curl_multi_ctx, rfds, wfds, efds, &fd_max);
+ *pfd_max = max_int(*pfd_max, fd_max);
+ curl_multi_timeout(curl_multi_ctx, &timeout);
+ if (timeout >= 0)
+ *ptimeout = min_int(*ptimeout, timeout);
+}
+
+void fs_net_event_loop(FSNetEventLoopCompletionFunc *cb, void *opaque)
+{
+ fd_set rfds, wfds, efds;
+ int timeout, fd_max;
+ struct timeval tv;
+
+ if (!curl_multi_ctx)
+ return;
+
+ for(;;) {
+ fd_max = -1;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+ timeout = 10000;
+ fs_net_set_fdset(&fd_max, &rfds, &wfds, &efds, &timeout);
+ if (cb) {
+ if (cb(opaque))
+ break;
+ } else {
+ if (list_empty(&xhr_list))
+ break;
+ }
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ select(fd_max + 1, &rfds, &wfds, &efds, &tv);
+ }
+}
+
+#endif /* !EMSCRIPTEN */
+
+XHRState *fs_wget(const char *url, const char *user, const char *password,
+ void *opaque, WGetWriteCallback *cb, BOOL single_write)
+{
+ return fs_wget2(url, user, password, NULL, 0, opaque, cb, single_write);
+}
+
+/***********************************************/
+/* file decryption */
+
+#define ENCRYPTED_FILE_HEADER_SIZE (4 + AES_BLOCK_SIZE)
+
+#define DEC_BUF_SIZE (256 * AES_BLOCK_SIZE)
+
+struct DecryptFileState {
+ DecryptFileCB *write_cb;
+ void *opaque;
+ int dec_state;
+ int dec_buf_pos;
+ AES_KEY *aes_state;
+ uint8_t iv[AES_BLOCK_SIZE];
+ uint8_t dec_buf[DEC_BUF_SIZE];
+};
+
+DecryptFileState *decrypt_file_init(AES_KEY *aes_state,
+ DecryptFileCB *write_cb,
+ void *opaque)
+{
+ DecryptFileState *s;
+ s = mallocz(sizeof(*s));
+ s->write_cb = write_cb;
+ s->opaque = opaque;
+ s->aes_state = aes_state;
+ return s;
+}
+
+int decrypt_file(DecryptFileState *s, const uint8_t *data,
+ size_t size)
+{
+ int l, len, ret;
+
+ while (size != 0) {
+ switch(s->dec_state) {
+ case 0:
+ l = min_int(size, ENCRYPTED_FILE_HEADER_SIZE - s->dec_buf_pos);
+ memcpy(s->dec_buf + s->dec_buf_pos, data, l);
+ s->dec_buf_pos += l;
+ if (s->dec_buf_pos >= ENCRYPTED_FILE_HEADER_SIZE) {
+ if (memcmp(s->dec_buf, encrypted_file_magic, 4) != 0)
+ return -1;
+ memcpy(s->iv, s->dec_buf + 4, AES_BLOCK_SIZE);
+ s->dec_state = 1;
+ s->dec_buf_pos = 0;
+ }
+ break;
+ case 1:
+ l = min_int(size, DEC_BUF_SIZE - s->dec_buf_pos);
+ memcpy(s->dec_buf + s->dec_buf_pos, data, l);
+ s->dec_buf_pos += l;
+ if (s->dec_buf_pos >= DEC_BUF_SIZE) {
+ /* keep one block in case it is the padding */
+ len = s->dec_buf_pos - AES_BLOCK_SIZE;
+ AES_cbc_encrypt(s->dec_buf, s->dec_buf, len,
+ s->aes_state, s->iv, FALSE);
+ ret = s->write_cb(s->opaque, s->dec_buf, len);
+ if (ret < 0)
+ return ret;
+ memcpy(s->dec_buf, s->dec_buf + s->dec_buf_pos - AES_BLOCK_SIZE,
+ AES_BLOCK_SIZE);
+ s->dec_buf_pos = AES_BLOCK_SIZE;
+ }
+ break;
+ default:
+ abort();
+ }
+ data += l;
+ size -= l;
+ }
+ return 0;
+}
+
+/* write last blocks */
+int decrypt_file_flush(DecryptFileState *s)
+{
+ int len, pad_len, ret;
+
+ if (s->dec_state != 1)
+ return -1;
+ len = s->dec_buf_pos;
+ if (len == 0 ||
+ (len % AES_BLOCK_SIZE) != 0)
+ return -1;
+ AES_cbc_encrypt(s->dec_buf, s->dec_buf, len,
+ s->aes_state, s->iv, FALSE);
+ pad_len = s->dec_buf[s->dec_buf_pos - 1];
+ if (pad_len < 1 || pad_len > AES_BLOCK_SIZE)
+ return -1;
+ len -= pad_len;
+ if (len != 0) {
+ ret = s->write_cb(s->opaque, s->dec_buf, len);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+void decrypt_file_end(DecryptFileState *s)
+{
+ free(s);
+}
+
+/* XHR file */
+
+typedef struct {
+ FSDevice *fs;
+ FSFile *f;
+ int64_t pos;
+ FSWGetFileCB *cb;
+ void *opaque;
+ FSFile *posted_file;
+ int64_t read_pos;
+ DecryptFileState *dec_state;
+} FSWGetFileState;
+
+static int fs_wget_file_write_cb(void *opaque, const uint8_t *data,
+ size_t size)
+{
+ FSWGetFileState *s = opaque;
+ FSDevice *fs = s->fs;
+ int ret;
+
+ ret = fs->fs_write(fs, s->f, s->pos, data, size);
+ if (ret < 0)
+ return ret;
+ s->pos += ret;
+ return ret;
+}
+
+static void fs_wget_file_on_load(void *opaque, int err, void *data, size_t size)
+{
+ FSWGetFileState *s = opaque;
+ FSDevice *fs = s->fs;
+ int ret;
+ int64_t ret_size;
+
+ // printf("err=%d size=%ld\n", err, size);
+ if (err < 0) {
+ ret_size = err;
+ goto done;
+ } else {
+ if (s->dec_state) {
+ ret = decrypt_file(s->dec_state, data, size);
+ if (ret >= 0 && err == 0) {
+ /* handle the end of file */
+ decrypt_file_flush(s->dec_state);
+ }
+ } else {
+ ret = fs_wget_file_write_cb(s, data, size);
+ }
+ if (ret < 0) {
+ ret_size = ret;
+ goto done;
+ } else if (err == 0) {
+ /* end of transfer */
+ ret_size = s->pos;
+ done:
+ s->cb(fs, s->f, ret_size, s->opaque);
+ if (s->dec_state)
+ decrypt_file_end(s->dec_state);
+ free(s);
+ }
+ }
+}
+
+static size_t fs_wget_file_read_cb(void *opaque, void *data, size_t size)
+{
+ FSWGetFileState *s = opaque;
+ FSDevice *fs = s->fs;
+ int ret;
+
+ if (!s->posted_file)
+ return 0;
+ ret = fs->fs_read(fs, s->posted_file, s->read_pos, data, size);
+ if (ret < 0)
+ return 0;
+ s->read_pos += ret;
+ return ret;
+}
+
+void fs_wget_file2(FSDevice *fs, FSFile *f, const char *url,
+ const char *user, const char *password,
+ FSFile *posted_file, uint64_t post_data_len,
+ FSWGetFileCB *cb, void *opaque,
+ AES_KEY *aes_state)
+{
+ FSWGetFileState *s;
+ s = mallocz(sizeof(*s));
+ s->fs = fs;
+ s->f = f;
+ s->pos = 0;
+ s->cb = cb;
+ s->opaque = opaque;
+ s->posted_file = posted_file;
+ s->read_pos = 0;
+ if (aes_state) {
+ s->dec_state = decrypt_file_init(aes_state, fs_wget_file_write_cb, s);
+ }
+
+ fs_wget2(url, user, password, fs_wget_file_read_cb, post_data_len,
+ s, fs_wget_file_on_load, FALSE);
+}
+
+/***********************************************/
+/* PBKDF2 */
+
+#ifdef USE_BUILTIN_CRYPTO
+
+#define HMAC_BLOCK_SIZE 64
+
+typedef struct {
+ SHA256_CTX ctx;
+ uint8_t K[HMAC_BLOCK_SIZE + SHA256_DIGEST_LENGTH];
+} HMAC_SHA256_CTX;
+
+void hmac_sha256_init(HMAC_SHA256_CTX *s, const uint8_t *key, int key_len)
+{
+ int i, l;
+
+ if (key_len > HMAC_BLOCK_SIZE) {
+ SHA256(key, key_len, s->K);
+ l = SHA256_DIGEST_LENGTH;
+ } else {
+ memcpy(s->K, key, key_len);
+ l = key_len;
+ }
+ memset(s->K + l, 0, HMAC_BLOCK_SIZE - l);
+ for(i = 0; i < HMAC_BLOCK_SIZE; i++)
+ s->K[i] ^= 0x36;
+ SHA256_Init(&s->ctx);
+ SHA256_Update(&s->ctx, s->K, HMAC_BLOCK_SIZE);
+}
+
+void hmac_sha256_update(HMAC_SHA256_CTX *s, const uint8_t *buf, int len)
+{
+ SHA256_Update(&s->ctx, buf, len);
+}
+
+/* out has a length of SHA256_DIGEST_LENGTH */
+void hmac_sha256_final(HMAC_SHA256_CTX *s, uint8_t *out)
+{
+ int i;
+
+ SHA256_Final(s->K + HMAC_BLOCK_SIZE, &s->ctx);
+ for(i = 0; i < HMAC_BLOCK_SIZE; i++)
+ s->K[i] ^= (0x36 ^ 0x5c);
+ SHA256(s->K, HMAC_BLOCK_SIZE + SHA256_DIGEST_LENGTH, out);
+}
+
+#define SALT_LEN_MAX 32
+
+void pbkdf2_hmac_sha256(const uint8_t *pwd, int pwd_len,
+ const uint8_t *salt, int salt_len,
+ int iter, int key_len, uint8_t *out)
+{
+ uint8_t F[SHA256_DIGEST_LENGTH], U[SALT_LEN_MAX + 4];
+ HMAC_SHA256_CTX ctx;
+ int it, U_len, j, l;
+ uint32_t i;
+
+ assert(salt_len <= SALT_LEN_MAX);
+ i = 1;
+ while (key_len > 0) {
+ memset(F, 0, SHA256_DIGEST_LENGTH);
+ memcpy(U, salt, salt_len);
+ U[salt_len] = i >> 24;
+ U[salt_len + 1] = i >> 16;
+ U[salt_len + 2] = i >> 8;
+ U[salt_len + 3] = i;
+ U_len = salt_len + 4;
+ for(it = 0; it < iter; it++) {
+ hmac_sha256_init(&ctx, pwd, pwd_len);
+ hmac_sha256_update(&ctx, U, U_len);
+ hmac_sha256_final(&ctx, U);
+ for(j = 0; j < SHA256_DIGEST_LENGTH; j++)
+ F[j] ^= U[j];
+ U_len = SHA256_DIGEST_LENGTH;
+ }
+ l = min_int(key_len, SHA256_DIGEST_LENGTH);
+ memcpy(out, F, l);
+ out += l;
+ key_len -= l;
+ i++;
+ }
+}
+
+#else
+
+void pbkdf2_hmac_sha256(const uint8_t *pwd, int pwd_len,
+ const uint8_t *salt, int salt_len,
+ int iter, int key_len, uint8_t *out)
+{
+ PKCS5_PBKDF2_HMAC((const char *)pwd, pwd_len, salt, salt_len,
+ iter, EVP_sha256(), key_len, out);
+}
+
+#endif /* !USE_BUILTIN_CRYPTO */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.h b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.h
new file mode 100644
index 0000000..35b6a4b
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.h
@@ -0,0 +1,93 @@
+/*
+ * HTTP file download
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#if defined(EMSCRIPTEN)
+#define USE_BUILTIN_CRYPTO
+#endif
+
+#ifdef USE_BUILTIN_CRYPTO
+#include "aes.h"
+#include "sha256.h"
+#else
+#include <openssl/aes.h>
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#endif
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+#define LOG() printf("%s:%d\n", __func__, __LINE__)
+
+/* XHR */
+
+/* err < 0: error (no data provided)
+ err = 0: end of transfer (data can be provided too)
+ err = 1: data chunk
+*/
+typedef void WGetWriteCallback(void *opaque, int err, void *data, size_t size);
+typedef size_t WGetReadCallback(void *opaque, void *data, size_t size);
+typedef struct XHRState XHRState;
+
+XHRState *fs_wget(const char *url, const char *user, const char *password,
+ void *opaque, WGetWriteCallback *cb, BOOL single_write);
+void fs_wget_free(XHRState *s);
+
+void fs_wget_init(void);
+void fs_wget_end(void);
+
+#ifndef EMSCRIPTEN
+typedef BOOL FSNetEventLoopCompletionFunc(void *opaque);
+void fs_net_set_fdset(int *pfd_max, fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int *ptimeout);
+void fs_net_event_loop(FSNetEventLoopCompletionFunc *cb, void *opaque);
+#endif
+
+/* crypto */
+
+extern const uint8_t encrypted_file_magic[4];
+
+typedef int DecryptFileCB(void *opaque, const uint8_t *data, size_t len);
+typedef struct DecryptFileState DecryptFileState;
+
+DecryptFileState *decrypt_file_init(AES_KEY *aes_state,
+ DecryptFileCB *write_cb,
+ void *opaque);
+int decrypt_file(DecryptFileState *s, const uint8_t *data,
+ size_t size);
+int decrypt_file_flush(DecryptFileState *s);
+void decrypt_file_end(DecryptFileState *s);
+
+void pbkdf2_hmac_sha256(const uint8_t *pwd, int pwd_len,
+ const uint8_t *salt, int salt_len,
+ int iter, int key_len, uint8_t *out);
+
+/* XHR file */
+
+typedef void FSWGetFileCB(FSDevice *fs, FSFile *f, int64_t size, void *opaque);
+
+void fs_wget_file2(FSDevice *fs, FSFile *f, const char *url,
+ const char *user, const char *password,
+ FSFile *posted_file, uint64_t post_data_len,
+ FSWGetFileCB *cb, void *opaque,
+ AES_KEY *aes_state);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/ide.c b/jslinux-2019-12-21/tinyemu-2019-12-21/ide.c
new file mode 100644
index 0000000..4952965
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/ide.c
@@ -0,0 +1,835 @@
+/*
+ * IDE emulation
+ *
+ * Copyright (c) 2003-2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "ide.h"
+
+//#define DEBUG_IDE
+
+/* Bits of HD_STATUS */
+#define ERR_STAT 0x01
+#define INDEX_STAT 0x02
+#define ECC_STAT 0x04 /* Corrected error */
+#define DRQ_STAT 0x08
+#define SEEK_STAT 0x10
+#define SRV_STAT 0x10
+#define WRERR_STAT 0x20
+#define READY_STAT 0x40
+#define BUSY_STAT 0x80
+
+/* Bits for HD_ERROR */
+#define MARK_ERR 0x01 /* Bad address mark */
+#define TRK0_ERR 0x02 /* couldn't find track 0 */
+#define ABRT_ERR 0x04 /* Command aborted */
+#define MCR_ERR 0x08 /* media change request */
+#define ID_ERR 0x10 /* ID field not found */
+#define MC_ERR 0x20 /* media changed */
+#define ECC_ERR 0x40 /* Uncorrectable ECC error */
+#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
+#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
+
+/* Bits of HD_NSECTOR */
+#define CD 0x01
+#define IO 0x02
+#define REL 0x04
+#define TAG_MASK 0xf8
+
+#define IDE_CMD_RESET 0x04
+#define IDE_CMD_DISABLE_IRQ 0x02
+
+/* ATA/ATAPI Commands pre T13 Spec */
+#define WIN_NOP 0x00
+/*
+ * 0x01->0x02 Reserved
+ */
+#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */
+/*
+ * 0x04->0x07 Reserved
+ */
+#define WIN_SRST 0x08 /* ATAPI soft reset command */
+#define WIN_DEVICE_RESET 0x08
+/*
+ * 0x09->0x0F Reserved
+ */
+#define WIN_RECAL 0x10
+#define WIN_RESTORE WIN_RECAL
+/*
+ * 0x10->0x1F Reserved
+ */
+#define WIN_READ 0x20 /* 28-Bit */
+#define WIN_READ_ONCE 0x21 /* 28-Bit without retries */
+#define WIN_READ_LONG 0x22 /* 28-Bit */
+#define WIN_READ_LONG_ONCE 0x23 /* 28-Bit without retries */
+#define WIN_READ_EXT 0x24 /* 48-Bit */
+#define WIN_READDMA_EXT 0x25 /* 48-Bit */
+#define WIN_READDMA_QUEUED_EXT 0x26 /* 48-Bit */
+#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */
+/*
+ * 0x28
+ */
+#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */
+/*
+ * 0x2A->0x2F Reserved
+ */
+#define WIN_WRITE 0x30 /* 28-Bit */
+#define WIN_WRITE_ONCE 0x31 /* 28-Bit without retries */
+#define WIN_WRITE_LONG 0x32 /* 28-Bit */
+#define WIN_WRITE_LONG_ONCE 0x33 /* 28-Bit without retries */
+#define WIN_WRITE_EXT 0x34 /* 48-Bit */
+#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */
+#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */
+#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */
+#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */
+#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */
+/*
+ * 0x3A->0x3B Reserved
+ */
+#define WIN_WRITE_VERIFY 0x3C /* 28-Bit */
+/*
+ * 0x3D->0x3F Reserved
+ */
+#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */
+#define WIN_VERIFY_ONCE 0x41 /* 28-Bit - without retries */
+#define WIN_VERIFY_EXT 0x42 /* 48-Bit */
+/*
+ * 0x43->0x4F Reserved
+ */
+#define WIN_FORMAT 0x50
+/*
+ * 0x51->0x5F Reserved
+ */
+#define WIN_INIT 0x60
+/*
+ * 0x61->0x5F Reserved
+ */
+#define WIN_SEEK 0x70 /* 0x70-0x7F Reserved */
+#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */
+#define WIN_DIAGNOSE 0x90
+#define WIN_SPECIFY 0x91 /* set drive geometry translation */
+#define WIN_DOWNLOAD_MICROCODE 0x92
+#define WIN_STANDBYNOW2 0x94
+#define WIN_STANDBY2 0x96
+#define WIN_SETIDLE2 0x97
+#define WIN_CHECKPOWERMODE2 0x98
+#define WIN_SLEEPNOW2 0x99
+/*
+ * 0x9A VENDOR
+ */
+#define WIN_PACKETCMD 0xA0 /* Send a packet command. */
+#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */
+#define WIN_QUEUED_SERVICE 0xA2
+#define WIN_SMART 0xB0 /* self-monitoring and reporting */
+#define CFA_ERASE_SECTORS 0xC0
+#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/
+#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */
+#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */
+#define WIN_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers */
+#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */
+#define WIN_READDMA_ONCE 0xC9 /* 28-Bit - without retries */
+#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */
+#define WIN_WRITEDMA_ONCE 0xCB /* 28-Bit - without retries */
+#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers */
+#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */
+#define WIN_GETMEDIASTATUS 0xDA
+#define WIN_ACKMEDIACHANGE 0xDB /* ATA-1, ATA-2 vendor */
+#define WIN_POSTBOOT 0xDC
+#define WIN_PREBOOT 0xDD
+#define WIN_DOORLOCK 0xDE /* lock door on removable drives */
+#define WIN_DOORUNLOCK 0xDF /* unlock door on removable drives */
+#define WIN_STANDBYNOW1 0xE0
+#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */
+#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */
+#define WIN_SETIDLE1 0xE3
+#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */
+#define WIN_CHECKPOWERMODE1 0xE5
+#define WIN_SLEEPNOW1 0xE6
+#define WIN_FLUSH_CACHE 0xE7
+#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */
+#define WIN_WRITE_SAME 0xE9 /* read ata-2 to use */
+ /* SET_FEATURES 0x22 or 0xDD */
+#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */
+#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
+#define WIN_MEDIAEJECT 0xED
+#define WIN_IDENTIFY_DMA 0xEE /* same as WIN_IDENTIFY, but DMA */
+#define WIN_SETFEATURES 0xEF /* set special drive features */
+#define EXABYTE_ENABLE_NEST 0xF0
+#define WIN_SECURITY_SET_PASS 0xF1
+#define WIN_SECURITY_UNLOCK 0xF2
+#define WIN_SECURITY_ERASE_PREPARE 0xF3
+#define WIN_SECURITY_ERASE_UNIT 0xF4
+#define WIN_SECURITY_FREEZE_LOCK 0xF5
+#define WIN_SECURITY_DISABLE 0xF6
+#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */
+#define WIN_SET_MAX 0xF9
+#define DISABLE_SEAGATE 0xFB
+
+#define MAX_MULT_SECTORS 128
+
+typedef struct IDEState IDEState;
+
+typedef void EndTransferFunc(IDEState *);
+
+struct IDEState {
+ IDEIFState *ide_if;
+ BlockDevice *bs;
+ int cylinders, heads, sectors;
+ int mult_sectors;
+ int64_t nb_sectors;
+
+ /* ide regs */
+ uint8_t feature;
+ uint8_t error;
+ uint16_t nsector; /* 0 is 256 to ease computations */
+ uint8_t sector;
+ uint8_t lcyl;
+ uint8_t hcyl;
+ uint8_t select;
+ uint8_t status;
+
+ int io_nb_sectors;
+ int req_nb_sectors;
+ EndTransferFunc *end_transfer_func;
+
+ int data_index;
+ int data_end;
+ uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
+};
+
+struct IDEIFState {
+ IRQSignal *irq;
+ IDEState *cur_drive;
+ IDEState *drives[2];
+ /* 0x3f6 command */
+ uint8_t cmd;
+};
+
+static void ide_sector_read_cb(void *opaque, int ret);
+static void ide_sector_read_cb_end(IDEState *s);
+static void ide_sector_write_cb2(void *opaque, int ret);
+
+static void padstr(char *str, const char *src, int len)
+{
+ int i, v;
+ for(i = 0; i < len; i++) {
+ if (*src)
+ v = *src++;
+ else
+ v = ' ';
+ *(char *)((long)str ^ 1) = v;
+ str++;
+ }
+}
+
+/* little endian assume */
+static void stw(uint16_t *buf, int v)
+{
+ *buf = v;
+}
+
+static void ide_identify(IDEState *s)
+{
+ uint16_t *tab;
+ uint32_t oldsize;
+
+ tab = (uint16_t *)s->io_buffer;
+
+ memset(tab, 0, 512 * 2);
+
+ stw(tab + 0, 0x0040);
+ stw(tab + 1, s->cylinders);
+ stw(tab + 3, s->heads);
+ stw(tab + 4, 512 * s->sectors); /* sectors */
+ stw(tab + 5, 512); /* sector size */
+ stw(tab + 6, s->sectors);
+ stw(tab + 20, 3); /* buffer type */
+ stw(tab + 21, 512); /* cache size in sectors */
+ stw(tab + 22, 4); /* ecc bytes */
+ padstr((char *)(tab + 27), "RISCVEMU HARDDISK", 40);
+ stw(tab + 47, 0x8000 | MAX_MULT_SECTORS);
+ stw(tab + 48, 0); /* dword I/O */
+ stw(tab + 49, 1 << 9); /* LBA supported, no DMA */
+ stw(tab + 51, 0x200); /* PIO transfer cycle */
+ stw(tab + 52, 0x200); /* DMA transfer cycle */
+ stw(tab + 54, s->cylinders);
+ stw(tab + 55, s->heads);
+ stw(tab + 56, s->sectors);
+ oldsize = s->cylinders * s->heads * s->sectors;
+ stw(tab + 57, oldsize);
+ stw(tab + 58, oldsize >> 16);
+ if (s->mult_sectors)
+ stw(tab + 59, 0x100 | s->mult_sectors);
+ stw(tab + 60, s->nb_sectors);
+ stw(tab + 61, s->nb_sectors >> 16);
+ stw(tab + 80, (1 << 1) | (1 << 2));
+ stw(tab + 82, (1 << 14));
+ stw(tab + 83, (1 << 14));
+ stw(tab + 84, (1 << 14));
+ stw(tab + 85, (1 << 14));
+ stw(tab + 86, 0);
+ stw(tab + 87, (1 << 14));
+}
+
+static void ide_set_signature(IDEState *s)
+{
+ s->select &= 0xf0;
+ s->nsector = 1;
+ s->sector = 1;
+ s->lcyl = 0;
+ s->hcyl = 0;
+}
+
+static void ide_abort_command(IDEState *s)
+{
+ s->status = READY_STAT | ERR_STAT;
+ s->error = ABRT_ERR;
+}
+
+static void ide_set_irq(IDEState *s)
+{
+ IDEIFState *ide_if = s->ide_if;
+ if (!(ide_if->cmd & IDE_CMD_DISABLE_IRQ)) {
+ set_irq(ide_if->irq, 1);
+ }
+}
+
+/* prepare data transfer and tell what to do after */
+static void ide_transfer_start(IDEState *s, int size,
+ EndTransferFunc *end_transfer_func)
+{
+ s->end_transfer_func = end_transfer_func;
+ s->data_index = 0;
+ s->data_end = size;
+}
+
+static void ide_transfer_stop(IDEState *s)
+{
+ s->end_transfer_func = ide_transfer_stop;
+ s->data_index = 0;
+ s->data_end = 0;
+}
+
+static int64_t ide_get_sector(IDEState *s)
+{
+ int64_t sector_num;
+ if (s->select & 0x40) {
+ /* lba */
+ sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
+ (s->lcyl << 8) | s->sector;
+ } else {
+ sector_num = ((s->hcyl << 8) | s->lcyl) *
+ s->heads * s->sectors +
+ (s->select & 0x0f) * s->sectors + (s->sector - 1);
+ }
+ return sector_num;
+}
+
+static void ide_set_sector(IDEState *s, int64_t sector_num)
+{
+ unsigned int cyl, r;
+ if (s->select & 0x40) {
+ s->select = (s->select & 0xf0) | ((sector_num >> 24) & 0x0f);
+ s->hcyl = (sector_num >> 16) & 0xff;
+ s->lcyl = (sector_num >> 8) & 0xff;
+ s->sector = sector_num & 0xff;
+ } else {
+ cyl = sector_num / (s->heads * s->sectors);
+ r = sector_num % (s->heads * s->sectors);
+ s->hcyl = (cyl >> 8) & 0xff;
+ s->lcyl = cyl & 0xff;
+ s->select = (s->select & 0xf0) | ((r / s->sectors) & 0x0f);
+ s->sector = (r % s->sectors) + 1;
+ }
+}
+
+static void ide_sector_read(IDEState *s)
+{
+ int64_t sector_num;
+ int ret, n;
+
+ sector_num = ide_get_sector(s);
+ n = s->nsector;
+ if (n == 0)
+ n = 256;
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+#if defined(DEBUG_IDE)
+ printf("read sector=%" PRId64 " count=%d\n", sector_num, n);
+#endif
+ s->io_nb_sectors = n;
+ ret = s->bs->read_async(s->bs, sector_num, s->io_buffer, n,
+ ide_sector_read_cb, s);
+ if (ret < 0) {
+ /* error */
+ ide_abort_command(s);
+ ide_set_irq(s);
+ } else if (ret == 0) {
+ /* synchronous case (needed for performance) */
+ ide_sector_read_cb(s, 0);
+ } else {
+ /* async case */
+ s->status = READY_STAT | SEEK_STAT | BUSY_STAT;
+ s->error = 0; /* not needed by IDE spec, but needed by Windows */
+ }
+}
+
+static void ide_sector_read_cb(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+ int n;
+ EndTransferFunc *func;
+
+ n = s->io_nb_sectors;
+ ide_set_sector(s, ide_get_sector(s) + n);
+ s->nsector = (s->nsector - n) & 0xff;
+ if (s->nsector == 0)
+ func = ide_sector_read_cb_end;
+ else
+ func = ide_sector_read;
+ ide_transfer_start(s, 512 * n, func);
+ ide_set_irq(s);
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+ s->error = 0; /* not needed by IDE spec, but needed by Windows */
+}
+
+static void ide_sector_read_cb_end(IDEState *s)
+{
+ /* no more sector to read from disk */
+ s->status = READY_STAT | SEEK_STAT;
+ s->error = 0; /* not needed by IDE spec, but needed by Windows */
+ ide_transfer_stop(s);
+}
+
+static void ide_sector_write_cb1(IDEState *s)
+{
+ int64_t sector_num;
+ int ret;
+
+ ide_transfer_stop(s);
+ sector_num = ide_get_sector(s);
+#if defined(DEBUG_IDE)
+ printf("write sector=%" PRId64 " count=%d\n",
+ sector_num, s->io_nb_sectors);
+#endif
+ ret = s->bs->write_async(s->bs, sector_num, s->io_buffer, s->io_nb_sectors,
+ ide_sector_write_cb2, s);
+ if (ret < 0) {
+ /* error */
+ ide_abort_command(s);
+ ide_set_irq(s);
+ } else if (ret == 0) {
+ /* synchronous case (needed for performance) */
+ ide_sector_write_cb2(s, 0);
+ } else {
+ /* async case */
+ s->status = READY_STAT | SEEK_STAT | BUSY_STAT;
+ }
+}
+
+static void ide_sector_write_cb2(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+ int n;
+
+ n = s->io_nb_sectors;
+ ide_set_sector(s, ide_get_sector(s) + n);
+ s->nsector = (s->nsector - n) & 0xff;
+ if (s->nsector == 0) {
+ /* no more sectors to write */
+ s->status = READY_STAT | SEEK_STAT;
+ } else {
+ n = s->nsector;
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+ s->io_nb_sectors = n;
+ ide_transfer_start(s, 512 * n, ide_sector_write_cb1);
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+ }
+ ide_set_irq(s);
+}
+
+static void ide_sector_write(IDEState *s)
+{
+ int n;
+ n = s->nsector;
+ if (n == 0)
+ n = 256;
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+ s->io_nb_sectors = n;
+ ide_transfer_start(s, 512 * n, ide_sector_write_cb1);
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+}
+
+static void ide_identify_cb(IDEState *s)
+{
+ ide_transfer_stop(s);
+ s->status = READY_STAT;
+}
+
+static void ide_exec_cmd(IDEState *s, int val)
+{
+#if defined(DEBUG_IDE)
+ printf("ide: exec_cmd=0x%02x\n", val);
+#endif
+ switch(val) {
+ case WIN_IDENTIFY:
+ ide_identify(s);
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+ ide_transfer_start(s, 512, ide_identify_cb);
+ ide_set_irq(s);
+ break;
+ case WIN_SPECIFY:
+ case WIN_RECAL:
+ s->error = 0;
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
+ case WIN_SETMULT:
+ if (s->nsector > MAX_MULT_SECTORS ||
+ (s->nsector & (s->nsector - 1)) != 0) {
+ ide_abort_command(s);
+ } else {
+ s->mult_sectors = s->nsector;
+#if defined(DEBUG_IDE)
+ printf("ide: setmult=%d\n", s->mult_sectors);
+#endif
+ s->status = READY_STAT;
+ }
+ ide_set_irq(s);
+ break;
+ case WIN_READ:
+ case WIN_READ_ONCE:
+ s->req_nb_sectors = 1;
+ ide_sector_read(s);
+ break;
+ case WIN_WRITE:
+ case WIN_WRITE_ONCE:
+ s->req_nb_sectors = 1;
+ ide_sector_write(s);
+ break;
+ case WIN_MULTREAD:
+ if (!s->mult_sectors) {
+ ide_abort_command(s);
+ ide_set_irq(s);
+ } else {
+ s->req_nb_sectors = s->mult_sectors;
+ ide_sector_read(s);
+ }
+ break;
+ case WIN_MULTWRITE:
+ if (!s->mult_sectors) {
+ ide_abort_command(s);
+ ide_set_irq(s);
+ } else {
+ s->req_nb_sectors = s->mult_sectors;
+ ide_sector_write(s);
+ }
+ break;
+ case WIN_READ_NATIVE_MAX:
+ ide_set_sector(s, s->nb_sectors - 1);
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
+ default:
+ ide_abort_command(s);
+ ide_set_irq(s);
+ break;
+ }
+}
+
+static void ide_ioport_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ IDEIFState *s1 = opaque;
+ IDEState *s = s1->cur_drive;
+ int addr = offset + 1;
+
+#ifdef DEBUG_IDE
+ printf("ide: write addr=0x%02x val=0x%02x\n", addr, val);
+#endif
+ switch(addr) {
+ case 0:
+ break;
+ case 1:
+ if (s) {
+ s->feature = val;
+ }
+ break;
+ case 2:
+ if (s) {
+ s->nsector = val;
+ }
+ break;
+ case 3:
+ if (s) {
+ s->sector = val;
+ }
+ break;
+ case 4:
+ if (s) {
+ s->lcyl = val;
+ }
+ break;
+ case 5:
+ if (s) {
+ s->hcyl = val;
+ }
+ break;
+ case 6:
+ /* select drive */
+ s = s1->cur_drive = s1->drives[(val >> 4) & 1];
+ if (s) {
+ s->select = val;
+ }
+ break;
+ default:
+ case 7:
+ /* command */
+ if (s) {
+ ide_exec_cmd(s, val);
+ }
+ break;
+ }
+}
+
+static uint32_t ide_ioport_read(void *opaque, uint32_t offset, int size_log2)
+{
+ IDEIFState *s1 = opaque;
+ IDEState *s = s1->cur_drive;
+ int ret, addr = offset + 1;
+
+ if (!s) {
+ ret = 0x00;
+ } else {
+ switch(addr) {
+ case 0:
+ ret = 0xff;
+ break;
+ case 1:
+ ret = s->error;
+ break;
+ case 2:
+ ret = s->nsector;
+ break;
+ case 3:
+ ret = s->sector;
+ break;
+ case 4:
+ ret = s->lcyl;
+ break;
+ case 5:
+ ret = s->hcyl;
+ break;
+ case 6:
+ ret = s->select;
+ break;
+ default:
+ case 7:
+ ret = s->status;
+ set_irq(s1->irq, 0);
+ break;
+ }
+ }
+#ifdef DEBUG_IDE
+ printf("ide: read addr=0x%02x val=0x%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+static uint32_t ide_status_read(void *opaque, uint32_t offset, int size_log2)
+{
+ IDEIFState *s1 = opaque;
+ IDEState *s = s1->cur_drive;
+ int ret;
+
+ if (s) {
+ ret = s->status;
+ } else {
+ ret = 0;
+ }
+#ifdef DEBUG_IDE
+ printf("ide: read status=0x%02x\n", ret);
+#endif
+ return ret;
+}
+
+static void ide_cmd_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ IDEIFState *s1 = opaque;
+ IDEState *s;
+ int i;
+
+#ifdef DEBUG_IDE
+ printf("ide: cmd write=0x%02x\n", val);
+#endif
+ if (!(s1->cmd & IDE_CMD_RESET) && (val & IDE_CMD_RESET)) {
+ /* low to high */
+ for(i = 0; i < 2; i++) {
+ s = s1->drives[i];
+ if (s) {
+ s->status = BUSY_STAT | SEEK_STAT;
+ s->error = 0x01;
+ }
+ }
+ } else if ((s1->cmd & IDE_CMD_RESET) && !(val & IDE_CMD_RESET)) {
+ /* high to low */
+ for(i = 0; i < 2; i++) {
+ s = s1->drives[i];
+ if (s) {
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_signature(s);
+ }
+ }
+ }
+ s1->cmd = val;
+}
+
+static void ide_data_writew(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ IDEIFState *s1 = opaque;
+ IDEState *s = s1->cur_drive;
+ int p;
+ uint8_t *tab;
+
+ if (!s)
+ return;
+ p = s->data_index;
+ tab = s->io_buffer;
+ tab[p] = val & 0xff;
+ tab[p + 1] = (val >> 8) & 0xff;
+ p += 2;
+ s->data_index = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+}
+
+static uint32_t ide_data_readw(void *opaque, uint32_t offset, int size_log2)
+{
+ IDEIFState *s1 = opaque;
+ IDEState *s = s1->cur_drive;
+ int p, ret;
+ uint8_t *tab;
+
+ if (!s) {
+ ret = 0;
+ } else {
+ p = s->data_index;
+ tab = s->io_buffer;
+ ret = tab[p] | (tab[p + 1] << 8);
+ p += 2;
+ s->data_index = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+ }
+ return ret;
+}
+
+static IDEState *ide_drive_init(IDEIFState *ide_if, BlockDevice *bs)
+{
+ IDEState *s;
+ uint32_t cylinders;
+ uint64_t nb_sectors;
+
+ s = malloc(sizeof(*s));
+ memset(s, 0, sizeof(*s));
+
+ s->ide_if = ide_if;
+ s->bs = bs;
+
+ nb_sectors = s->bs->get_sector_count(s->bs);
+ cylinders = nb_sectors / (16 * 63);
+ if (cylinders > 16383)
+ cylinders = 16383;
+ else if (cylinders < 2)
+ cylinders = 2;
+ s->cylinders = cylinders;
+ s->heads = 16;
+ s->sectors = 63;
+ s->nb_sectors = nb_sectors;
+
+ s->mult_sectors = MAX_MULT_SECTORS;
+ /* ide regs */
+ s->feature = 0;
+ s->error = 0;
+ s->nsector = 0;
+ s->sector = 0;
+ s->lcyl = 0;
+ s->hcyl = 0;
+ s->select = 0xa0;
+ s->status = READY_STAT | SEEK_STAT;
+
+ /* init I/O buffer */
+ s->data_index = 0;
+ s->data_end = 0;
+ s->end_transfer_func = ide_transfer_stop;
+
+ s->req_nb_sectors = 0; /* temp for read/write */
+ s->io_nb_sectors = 0; /* temp for read/write */
+ return s;
+}
+
+IDEIFState *ide_init(PhysMemoryMap *port_map, uint32_t addr, uint32_t addr2,
+ IRQSignal *irq, BlockDevice **tab_bs)
+{
+ int i;
+ IDEIFState *s;
+
+ s = malloc(sizeof(IDEIFState));
+ memset(s, 0, sizeof(*s));
+
+ s->irq = irq;
+ s->cmd = 0;
+
+ cpu_register_device(port_map, addr, 1, s, ide_data_readw, ide_data_writew,
+ DEVIO_SIZE16);
+ cpu_register_device(port_map, addr + 1, 7, s, ide_ioport_read, ide_ioport_write,
+ DEVIO_SIZE8);
+ if (addr2) {
+ cpu_register_device(port_map, addr2, 1, s, ide_status_read, ide_cmd_write,
+ DEVIO_SIZE8);
+ }
+
+ for(i = 0; i < 2; i++) {
+ if (tab_bs[i])
+ s->drives[i] = ide_drive_init(s, tab_bs[i]);
+ }
+ s->cur_drive = s->drives[0];
+ return s;
+}
+
+/* dummy PCI device for the IDE */
+PCIDevice *piix3_ide_init(PCIBus *pci_bus, int devfn)
+{
+ PCIDevice *d;
+ d = pci_register_device(pci_bus, "PIIX3 IDE", devfn, 0x8086, 0x7010, 0x00, 0x0101);
+ pci_device_set_config8(d, 0x09, 0x00); /* ISA IDE ports, no DMA */
+ return d;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/ide.h b/jslinux-2019-12-21/tinyemu-2019-12-21/ide.h
new file mode 100644
index 0000000..257a6a0
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/ide.h
@@ -0,0 +1,32 @@
+/*
+ * IDE emulation
+ *
+ * Copyright (c) 2003-2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "virtio.h"
+#include "iomem.h"
+#include "pci.h"
+
+typedef struct IDEIFState IDEIFState;
+
+IDEIFState *ide_init(PhysMemoryMap *port_map, uint32_t addr, uint32_t addr2,
+ IRQSignal *irq, BlockDevice **tab_bs);
+PCIDevice *piix3_ide_init(PCIBus *pci_bus, int devfn);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/iomem.c b/jslinux-2019-12-21/tinyemu-2019-12-21/iomem.c
new file mode 100644
index 0000000..c63ada0
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/iomem.c
@@ -0,0 +1,264 @@
+/*
+ * IO memory handling
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "iomem.h"
+
+static PhysMemoryRange *default_register_ram(PhysMemoryMap *s, uint64_t addr,
+ uint64_t size, int devram_flags);
+static void default_free_ram(PhysMemoryMap *s, PhysMemoryRange *pr);
+static const uint32_t *default_get_dirty_bits(PhysMemoryMap *map, PhysMemoryRange *pr);
+static void default_set_addr(PhysMemoryMap *map,
+ PhysMemoryRange *pr, uint64_t addr, BOOL enabled);
+
+PhysMemoryMap *phys_mem_map_init(void)
+{
+ PhysMemoryMap *s;
+ s = mallocz(sizeof(*s));
+ s->register_ram = default_register_ram;
+ s->free_ram = default_free_ram;
+ s->get_dirty_bits = default_get_dirty_bits;
+ s->set_ram_addr = default_set_addr;
+ return s;
+}
+
+void phys_mem_map_end(PhysMemoryMap *s)
+{
+ int i;
+ PhysMemoryRange *pr;
+
+ for(i = 0; i < s->n_phys_mem_range; i++) {
+ pr = &s->phys_mem_range[i];
+ if (pr->is_ram) {
+ s->free_ram(s, pr);
+ }
+ }
+ free(s);
+}
+
+/* return NULL if not found */
+/* XXX: optimize */
+PhysMemoryRange *get_phys_mem_range(PhysMemoryMap *s, uint64_t paddr)
+{
+ PhysMemoryRange *pr;
+ int i;
+ for(i = 0; i < s->n_phys_mem_range; i++) {
+ pr = &s->phys_mem_range[i];
+ if (paddr >= pr->addr && paddr < pr->addr + pr->size)
+ return pr;
+ }
+ return NULL;
+}
+
+PhysMemoryRange *register_ram_entry(PhysMemoryMap *s, uint64_t addr,
+ uint64_t size, int devram_flags)
+{
+ PhysMemoryRange *pr;
+
+ assert(s->n_phys_mem_range < PHYS_MEM_RANGE_MAX);
+ assert((size & (DEVRAM_PAGE_SIZE - 1)) == 0 && size != 0);
+ pr = &s->phys_mem_range[s->n_phys_mem_range++];
+ pr->map = s;
+ pr->is_ram = TRUE;
+ pr->devram_flags = devram_flags & ~DEVRAM_FLAG_DISABLED;
+ pr->addr = addr;
+ pr->org_size = size;
+ if (devram_flags & DEVRAM_FLAG_DISABLED)
+ pr->size = 0;
+ else
+ pr->size = pr->org_size;
+ pr->phys_mem = NULL;
+ pr->dirty_bits = NULL;
+ return pr;
+}
+
+static PhysMemoryRange *default_register_ram(PhysMemoryMap *s, uint64_t addr,
+ uint64_t size, int devram_flags)
+{
+ PhysMemoryRange *pr;
+
+ pr = register_ram_entry(s, addr, size, devram_flags);
+
+ pr->phys_mem = mallocz(size);
+ if (!pr->phys_mem) {
+ fprintf(stderr, "Could not allocate VM memory\n");
+ exit(1);
+ }
+
+ if (devram_flags & DEVRAM_FLAG_DIRTY_BITS) {
+ size_t nb_pages;
+ int i;
+ nb_pages = size >> DEVRAM_PAGE_SIZE_LOG2;
+ pr->dirty_bits_size = ((nb_pages + 31) / 32) * sizeof(uint32_t);
+ pr->dirty_bits_index = 0;
+ for(i = 0; i < 2; i++) {
+ pr->dirty_bits_tab[i] = mallocz(pr->dirty_bits_size);
+ }
+ pr->dirty_bits = pr->dirty_bits_tab[pr->dirty_bits_index];
+ }
+ return pr;
+}
+
+/* return a pointer to the bitmap of dirty bits and reset them */
+static const uint32_t *default_get_dirty_bits(PhysMemoryMap *map,
+ PhysMemoryRange *pr)
+{
+ uint32_t *dirty_bits;
+ BOOL has_dirty_bits;
+ size_t n, i;
+
+ dirty_bits = pr->dirty_bits;
+
+ has_dirty_bits = FALSE;
+ n = pr->dirty_bits_size / sizeof(uint32_t);
+ for(i = 0; i < n; i++) {
+ if (dirty_bits[i] != 0) {
+ has_dirty_bits = TRUE;
+ break;
+ }
+ }
+ if (has_dirty_bits && pr->size != 0) {
+ /* invalidate the corresponding CPU write TLBs */
+ map->flush_tlb_write_range(map->opaque, pr->phys_mem, pr->org_size);
+ }
+
+ pr->dirty_bits_index ^= 1;
+ pr->dirty_bits = pr->dirty_bits_tab[pr->dirty_bits_index];
+ memset(pr->dirty_bits, 0, pr->dirty_bits_size);
+ return dirty_bits;
+}
+
+/* reset the dirty bit of one page at 'offset' inside 'pr' */
+void phys_mem_reset_dirty_bit(PhysMemoryRange *pr, size_t offset)
+{
+ size_t page_index;
+ uint32_t mask, *dirty_bits_ptr;
+ PhysMemoryMap *map;
+ if (pr->dirty_bits) {
+ page_index = offset >> DEVRAM_PAGE_SIZE_LOG2;
+ mask = 1 << (page_index & 0x1f);
+ dirty_bits_ptr = pr->dirty_bits + (page_index >> 5);
+ if (*dirty_bits_ptr & mask) {
+ *dirty_bits_ptr &= ~mask;
+ /* invalidate the corresponding CPU write TLBs */
+ map = pr->map;
+ map->flush_tlb_write_range(map->opaque,
+ pr->phys_mem + (offset & ~(DEVRAM_PAGE_SIZE - 1)),
+ DEVRAM_PAGE_SIZE);
+ }
+ }
+}
+
+static void default_free_ram(PhysMemoryMap *s, PhysMemoryRange *pr)
+{
+ free(pr->phys_mem);
+}
+
+PhysMemoryRange *cpu_register_device(PhysMemoryMap *s, uint64_t addr,
+ uint64_t size, void *opaque,
+ DeviceReadFunc *read_func, DeviceWriteFunc *write_func,
+ int devio_flags)
+{
+ PhysMemoryRange *pr;
+ assert(s->n_phys_mem_range < PHYS_MEM_RANGE_MAX);
+ assert(size <= 0xffffffff);
+ pr = &s->phys_mem_range[s->n_phys_mem_range++];
+ pr->map = s;
+ pr->addr = addr;
+ pr->org_size = size;
+ if (devio_flags & DEVIO_DISABLED)
+ pr->size = 0;
+ else
+ pr->size = pr->org_size;
+ pr->is_ram = FALSE;
+ pr->opaque = opaque;
+ pr->read_func = read_func;
+ pr->write_func = write_func;
+ pr->devio_flags = devio_flags;
+ return pr;
+}
+
+static void default_set_addr(PhysMemoryMap *map,
+ PhysMemoryRange *pr, uint64_t addr, BOOL enabled)
+{
+ if (enabled) {
+ if (pr->size == 0 || pr->addr != addr) {
+ /* enable or move mapping */
+ if (pr->is_ram) {
+ map->flush_tlb_write_range(map->opaque,
+ pr->phys_mem, pr->org_size);
+ }
+ pr->addr = addr;
+ pr->size = pr->org_size;
+ }
+ } else {
+ if (pr->size != 0) {
+ /* disable mapping */
+ if (pr->is_ram) {
+ map->flush_tlb_write_range(map->opaque,
+ pr->phys_mem, pr->org_size);
+ }
+ pr->addr = 0;
+ pr->size = 0;
+ }
+ }
+}
+
+void phys_mem_set_addr(PhysMemoryRange *pr, uint64_t addr, BOOL enabled)
+{
+ PhysMemoryMap *map = pr->map;
+ if (!pr->is_ram) {
+ default_set_addr(map, pr, addr, enabled);
+ } else {
+ return map->set_ram_addr(map, pr, addr, enabled);
+ }
+}
+
+/* return NULL if no valid RAM page. The access can only be done in the page */
+uint8_t *phys_mem_get_ram_ptr(PhysMemoryMap *map, uint64_t paddr, BOOL is_rw)
+{
+ PhysMemoryRange *pr = get_phys_mem_range(map, paddr);
+ uintptr_t offset;
+ if (!pr || !pr->is_ram)
+ return NULL;
+ offset = paddr - pr->addr;
+ if (is_rw)
+ phys_mem_set_dirty_bit(pr, offset);
+ return pr->phys_mem + (uintptr_t)offset;
+}
+
+/* IRQ support */
+
+void irq_init(IRQSignal *irq, SetIRQFunc *set_irq, void *opaque, int irq_num)
+{
+ irq->set_irq = set_irq;
+ irq->opaque = opaque;
+ irq->irq_num = irq_num;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/iomem.h b/jslinux-2019-12-21/tinyemu-2019-12-21/iomem.h
new file mode 100644
index 0000000..cc561b4
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/iomem.h
@@ -0,0 +1,148 @@
+/*
+ * IO memory handling
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef IOMEM_H
+#define IOMEM_H
+
+typedef void DeviceWriteFunc(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2);
+typedef uint32_t DeviceReadFunc(void *opaque, uint32_t offset, int size_log2);
+
+#define DEVIO_SIZE8 (1 << 0)
+#define DEVIO_SIZE16 (1 << 1)
+#define DEVIO_SIZE32 (1 << 2)
+/* not supported, could add specific 64 bit callbacks when needed */
+//#define DEVIO_SIZE64 (1 << 3)
+#define DEVIO_DISABLED (1 << 4)
+
+#define DEVRAM_FLAG_ROM (1 << 0) /* not writable */
+#define DEVRAM_FLAG_DIRTY_BITS (1 << 1) /* maintain dirty bits */
+#define DEVRAM_FLAG_DISABLED (1 << 2) /* allocated but not mapped */
+#define DEVRAM_PAGE_SIZE_LOG2 12
+#define DEVRAM_PAGE_SIZE (1 << DEVRAM_PAGE_SIZE_LOG2)
+
+typedef struct PhysMemoryMap PhysMemoryMap;
+
+typedef struct {
+ PhysMemoryMap *map;
+ uint64_t addr;
+ uint64_t org_size; /* original size */
+ uint64_t size; /* =org_size or 0 if the mapping is disabled */
+ BOOL is_ram;
+ /* the following is used for RAM access */
+ int devram_flags;
+ uint8_t *phys_mem;
+ int dirty_bits_size; /* in bytes */
+ uint32_t *dirty_bits; /* NULL if not used */
+ uint32_t *dirty_bits_tab[2];
+ int dirty_bits_index; /* 0-1 */
+ /* the following is used for I/O access */
+ void *opaque;
+ DeviceReadFunc *read_func;
+ DeviceWriteFunc *write_func;
+ int devio_flags;
+} PhysMemoryRange;
+
+#define PHYS_MEM_RANGE_MAX 32
+
+struct PhysMemoryMap {
+ int n_phys_mem_range;
+ PhysMemoryRange phys_mem_range[PHYS_MEM_RANGE_MAX];
+ PhysMemoryRange *(*register_ram)(PhysMemoryMap *s, uint64_t addr,
+ uint64_t size, int devram_flags);
+ void (*free_ram)(PhysMemoryMap *s, PhysMemoryRange *pr);
+ const uint32_t *(*get_dirty_bits)(PhysMemoryMap *s, PhysMemoryRange *pr);
+ void (*set_ram_addr)(PhysMemoryMap *s, PhysMemoryRange *pr, uint64_t addr,
+ BOOL enabled);
+ void *opaque;
+ void (*flush_tlb_write_range)(void *opaque, uint8_t *ram_addr,
+ size_t ram_size);
+};
+
+
+PhysMemoryMap *phys_mem_map_init(void);
+void phys_mem_map_end(PhysMemoryMap *s);
+PhysMemoryRange *register_ram_entry(PhysMemoryMap *s, uint64_t addr,
+ uint64_t size, int devram_flags);
+static inline PhysMemoryRange *cpu_register_ram(PhysMemoryMap *s, uint64_t addr,
+ uint64_t size, int devram_flags)
+{
+ return s->register_ram(s, addr, size, devram_flags);
+}
+PhysMemoryRange *cpu_register_device(PhysMemoryMap *s, uint64_t addr,
+ uint64_t size, void *opaque,
+ DeviceReadFunc *read_func, DeviceWriteFunc *write_func,
+ int devio_flags);
+PhysMemoryRange *get_phys_mem_range(PhysMemoryMap *s, uint64_t paddr);
+void phys_mem_set_addr(PhysMemoryRange *pr, uint64_t addr, BOOL enabled);
+
+static inline const uint32_t *phys_mem_get_dirty_bits(PhysMemoryRange *pr)
+{
+ PhysMemoryMap *map = pr->map;
+ return map->get_dirty_bits(map, pr);
+}
+
+static inline void phys_mem_set_dirty_bit(PhysMemoryRange *pr, size_t offset)
+{
+ size_t page_index;
+ uint32_t mask, *dirty_bits_ptr;
+ if (pr->dirty_bits) {
+ page_index = offset >> DEVRAM_PAGE_SIZE_LOG2;
+ mask = 1 << (page_index & 0x1f);
+ dirty_bits_ptr = pr->dirty_bits + (page_index >> 5);
+ *dirty_bits_ptr |= mask;
+ }
+}
+
+static inline BOOL phys_mem_is_dirty_bit(PhysMemoryRange *pr, size_t offset)
+{
+ size_t page_index;
+ uint32_t *dirty_bits_ptr;
+ if (!pr->dirty_bits)
+ return TRUE;
+ page_index = offset >> DEVRAM_PAGE_SIZE_LOG2;
+ dirty_bits_ptr = pr->dirty_bits + (page_index >> 5);
+ return (*dirty_bits_ptr >> (page_index & 0x1f)) & 1;
+}
+
+void phys_mem_reset_dirty_bit(PhysMemoryRange *pr, size_t offset);
+uint8_t *phys_mem_get_ram_ptr(PhysMemoryMap *map, uint64_t paddr, BOOL is_rw);
+
+/* IRQ support */
+
+typedef void SetIRQFunc(void *opaque, int irq_num, int level);
+
+typedef struct {
+ SetIRQFunc *set_irq;
+ void *opaque;
+ int irq_num;
+} IRQSignal;
+
+void irq_init(IRQSignal *irq, SetIRQFunc *set_irq, void *opaque, int irq_num);
+
+static inline void set_irq(IRQSignal *irq, int level)
+{
+ irq->set_irq(irq->opaque, irq->irq_num, level);
+}
+
+#endif /* IOMEM_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/js/lib.js b/jslinux-2019-12-21/tinyemu-2019-12-21/js/lib.js
new file mode 100644
index 0000000..5dbf2b2
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/js/lib.js
@@ -0,0 +1,280 @@
+/*
+ * JS emulator library
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+mergeInto(LibraryManager.library, {
+ console_write: function(opaque, buf, len)
+ {
+ var str;
+ /* Note: we really send byte values. It would be up to the
+ * terminal to support UTF-8 */
+ str = String.fromCharCode.apply(String, HEAPU8.subarray(buf, buf + len));
+ term.write(str);
+ },
+
+ console_get_size: function(pw, ph)
+ {
+ var w, h, r;
+ r = term.getSize();
+ HEAPU32[pw >> 2] = r[0];
+ HEAPU32[ph >> 2] = r[1];
+ },
+
+ fs_export_file: function(filename, buf, buf_len)
+ {
+ var _filename = Pointer_stringify(filename);
+// console.log("exporting " + _filename);
+ var data = HEAPU8.subarray(buf, buf + buf_len);
+ var file = new Blob([data], { type: "application/octet-stream" });
+ var url = URL.createObjectURL(file);
+ var a = document.createElement("a");
+ a.href = url;
+ a.setAttribute("download", _filename);
+ a.innerHTML = "downloading";
+ document.body.appendChild(a);
+ /* click on the link and remove it */
+ setTimeout(function() {
+ a.click();
+ document.body.removeChild(a);
+ }, 50);
+ },
+
+ emscripten_async_wget3_data: function(url, request, user, password, post_data, post_data_len, arg, free, onload, onerror, onprogress) {
+ var _url = Pointer_stringify(url);
+ var _request = Pointer_stringify(request);
+ var _user;
+ var _password;
+
+ var http = new XMLHttpRequest();
+
+ if (user)
+ _user = Pointer_stringify(user);
+ else
+ _user = null;
+ if (password)
+ _password = Pointer_stringify(password);
+ else
+ _password = null;
+
+ http.open(_request, _url, true);
+ http.responseType = 'arraybuffer';
+ if (_user) {
+ http.setRequestHeader("Authorization", "Basic " + btoa(_user + ':' + _password));
+ }
+
+ var handle = Browser.getNextWgetRequestHandle();
+
+ // LOAD
+ http.onload = function http_onload(e) {
+ if (http.status == 200 || _url.substr(0,4).toLowerCase() != "http") {
+ var byteArray = new Uint8Array(http.response);
+ var buffer = _malloc(byteArray.length);
+ HEAPU8.set(byteArray, buffer);
+ if (onload) Runtime.dynCall('viiii', onload, [handle, arg, buffer, byteArray.length]);
+ if (free) _free(buffer);
+ } else {
+ if (onerror) Runtime.dynCall('viiii', onerror, [handle, arg, http.status, http.statusText]);
+ }
+ delete Browser.wgetRequests[handle];
+ };
+
+ // ERROR
+ http.onerror = function http_onerror(e) {
+ if (onerror) {
+ Runtime.dynCall('viiii', onerror, [handle, arg, http.status, http.statusText]);
+ }
+ delete Browser.wgetRequests[handle];
+ };
+
+ // PROGRESS
+ http.onprogress = function http_onprogress(e) {
+ if (onprogress) Runtime.dynCall('viiii', onprogress, [handle, arg, e.loaded, e.lengthComputable || e.lengthComputable === undefined ? e.total : 0]);
+ };
+
+ // ABORT
+ http.onabort = function http_onabort(e) {
+ delete Browser.wgetRequests[handle];
+ };
+
+ // Useful because the browser can limit the number of redirection
+ try {
+ if (http.channel instanceof Ci.nsIHttpChannel)
+ http.channel.redirectionLimit = 0;
+ } catch (ex) { /* whatever */ }
+
+ if (_request == "POST") {
+ var _post_data = HEAPU8.subarray(post_data, post_data + post_data_len);
+ //Send the proper header information along with the request
+ http.setRequestHeader("Content-type", "application/octet-stream");
+ http.setRequestHeader("Content-length", post_data_len);
+ http.setRequestHeader("Connection", "close");
+ http.send(_post_data);
+ } else {
+ http.send(null);
+ }
+
+ Browser.wgetRequests[handle] = http;
+
+ return handle;
+ },
+
+ fs_wget_update_downloading: function (flag)
+ {
+ update_downloading(Boolean(flag));
+ },
+
+ fb_refresh: function(opaque, data, x, y, w, h, stride)
+ {
+ var i, j, v, src, image_data, dst_pos, display, dst_pos1, image_stride;
+
+ display = graphic_display;
+ /* current x = 0 and w = width for all refreshes */
+// console.log("fb_refresh: x=" + x + " y=" + y + " w=" + w + " h=" + h);
+ image_data = display.image.data;
+ image_stride = display.width * 4;
+ dst_pos1 = (y * display.width + x) * 4;
+ for(i = 0; i < h; i = (i + 1) | 0) {
+ src = data;
+ dst_pos = dst_pos1;
+ for(j = 0; j < w; j = (j + 1) | 0) {
+ v = HEAPU32[src >> 2];
+ image_data[dst_pos] = (v >> 16) & 0xff;
+ image_data[dst_pos + 1] = (v >> 8) & 0xff;
+ image_data[dst_pos + 2] = v & 0xff;
+ image_data[dst_pos + 3] = 0xff; /* XXX: do it once */
+ src = (src + 4) | 0;
+ dst_pos = (dst_pos + 4) | 0;
+ }
+ data = (data + stride) | 0;
+ dst_pos1 = (dst_pos1 + image_stride) | 0;
+ }
+ display.ctx.putImageData(display.image, 0, 0, x, y, w, h);
+ },
+
+ net_recv_packet: function(bs, buf, buf_len)
+ {
+ if (net_state) {
+ net_state.recv_packet(HEAPU8.subarray(buf, buf + buf_len));
+ }
+ },
+
+ /* file buffer API */
+ file_buffer_get_new_handle: function()
+ {
+ var h;
+ if (typeof Browser.fbuf_table == "undefined") {
+ Browser.fbuf_table = new Object();
+ Browser.fbuf_next_handle = 1;
+ }
+ for(;;) {
+ h = Browser.fbuf_next_handle;
+ Browser.fbuf_next_handle++;
+ if (Browser.fbuf_next_handle == 0x80000000)
+ Browser.fbuf_next_handle = 1;
+ if (typeof Browser.fbuf_table[h] == "undefined") {
+// console.log("new handle=" + h);
+ return h;
+ }
+ }
+ },
+
+ file_buffer_init: function(bs)
+ {
+ var h;
+ HEAPU32[bs >> 2] = 0;
+ HEAPU32[(bs + 4) >> 2] = 0;
+ },
+
+ file_buffer_resize__deps: ['file_buffer_get_new_handle'],
+ file_buffer_resize: function(bs, new_size)
+ {
+ var h, size, new_data, size1, i, data;
+ h = HEAPU32[bs >> 2];
+ size = HEAPU32[(bs + 4) >> 2];
+ if (new_size == 0) {
+ if (h != 0) {
+ delete Browser.fbuf_table[h];
+ h = 0;
+ }
+ } else if (size == 0) {
+ h = _file_buffer_get_new_handle();
+ new_data = new Uint8Array(new_size);
+ Browser.fbuf_table[h] = new_data;
+ } else if (size != new_size) {
+ data = Browser.fbuf_table[h];
+ new_data = new Uint8Array(new_size);
+ if (new_size > size) {
+ new_data.set(data, 0);
+ } else {
+ for(i = 0; i < new_size; i = (i + 1) | 0)
+ new_data[i] = data[i];
+ }
+ Browser.fbuf_table[h] = new_data;
+ }
+ HEAPU32[bs >> 2] = h;
+ HEAPU32[(bs + 4) >> 2] = new_size;
+ return 0;
+ },
+
+ file_buffer_reset: function(bs)
+ {
+ _file_buffer_resize(bs, 0);
+ _file_buffer_init(bs);
+ },
+
+ file_buffer_write: function(bs, offset, buf, size)
+ {
+ var h, data, i;
+ h = HEAPU32[bs >> 2];
+ if (h) {
+ data = Browser.fbuf_table[h];
+ for(i = 0; i < size; i = (i + 1) | 0) {
+ data[offset + i] = HEAPU8[buf + i];
+ }
+ }
+ },
+
+ file_buffer_read: function(bs, offset, buf, size)
+ {
+ var h, data, i;
+ h = HEAPU32[bs >> 2];
+ if (h) {
+ data = Browser.fbuf_table[h];
+ for(i = 0; i < size; i = (i + 1) | 0) {
+ HEAPU8[buf + i] = data[offset + i];
+ }
+ }
+ },
+
+ file_buffer_set: function(bs, offset, val, size)
+ {
+ var h, data, i;
+ h = HEAPU32[bs >> 2];
+ if (h) {
+ data = Browser.fbuf_table[h];
+ for(i = 0; i < size; i = (i + 1) | 0) {
+ data[offset + i] = val;
+ }
+ }
+ },
+
+});
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c b/jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c
new file mode 100644
index 0000000..3641db4
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c
@@ -0,0 +1,349 @@
+/*
+ * JS emulator main
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <emscripten.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "virtio.h"
+#include "machine.h"
+#include "list.h"
+#include "fbuf.h"
+
+void virt_machine_run(void *opaque);
+
+/* provided in lib.js */
+extern void console_write(void *opaque, const uint8_t *buf, int len);
+extern void console_get_size(int *pw, int *ph);
+extern void fb_refresh(void *opaque, void *data,
+ int x, int y, int w, int h, int stride);
+extern void net_recv_packet(EthernetDevice *bs,
+ const uint8_t *buf, int len);
+
+static uint8_t console_fifo[1024];
+static int console_fifo_windex;
+static int console_fifo_rindex;
+static int console_fifo_count;
+static BOOL console_resize_pending;
+
+static int global_width;
+static int global_height;
+static VirtMachine *global_vm;
+static BOOL global_carrier_state;
+
+static int console_read(void *opaque, uint8_t *buf, int len)
+{
+ int out_len, l;
+ len = min_int(len, console_fifo_count);
+ console_fifo_count -= len;
+ out_len = 0;
+ while (len != 0) {
+ l = min_int(len, sizeof(console_fifo) - console_fifo_rindex);
+ memcpy(buf + out_len, console_fifo + console_fifo_rindex, l);
+ len -= l;
+ out_len += l;
+ console_fifo_rindex += l;
+ if (console_fifo_rindex == sizeof(console_fifo))
+ console_fifo_rindex = 0;
+ }
+ return out_len;
+}
+
+/* called from JS */
+void console_queue_char(int c)
+{
+ if (console_fifo_count < sizeof(console_fifo)) {
+ console_fifo[console_fifo_windex] = c;
+ if (++console_fifo_windex == sizeof(console_fifo))
+ console_fifo_windex = 0;
+ console_fifo_count++;
+ }
+}
+
+/* called from JS */
+void display_key_event(int is_down, int key_code)
+{
+ if (global_vm) {
+ vm_send_key_event(global_vm, is_down, key_code);
+ }
+}
+
+/* called from JS */
+static int mouse_last_x, mouse_last_y, mouse_last_buttons;
+
+void display_mouse_event(int dx, int dy, int buttons)
+{
+ if (global_vm) {
+ if (vm_mouse_is_absolute(global_vm) || 1) {
+ dx = min_int(dx, global_width - 1);
+ dy = min_int(dy, global_height - 1);
+ dx = (dx * VIRTIO_INPUT_ABS_SCALE) / global_width;
+ dy = (dy * VIRTIO_INPUT_ABS_SCALE) / global_height;
+ } else {
+ /* relative mouse is not supported */
+ dx = 0;
+ dy = 0;
+ }
+ mouse_last_x = dx;
+ mouse_last_y = dy;
+ mouse_last_buttons = buttons;
+ vm_send_mouse_event(global_vm, dx, dy, 0, buttons);
+ }
+}
+
+/* called from JS */
+void display_wheel_event(int dz)
+{
+ if (global_vm) {
+ vm_send_mouse_event(global_vm, mouse_last_x, mouse_last_y, dz,
+ mouse_last_buttons);
+ }
+}
+
+/* called from JS */
+void net_write_packet(const uint8_t *buf, int buf_len)
+{
+ EthernetDevice *net = global_vm->net;
+ if (net) {
+ net->device_write_packet(net, buf, buf_len);
+ }
+}
+
+/* called from JS */
+void net_set_carrier(BOOL carrier_state)
+{
+ EthernetDevice *net;
+ global_carrier_state = carrier_state;
+ if (global_vm && global_vm->net) {
+ net = global_vm->net;
+ net->device_set_carrier(net, carrier_state);
+ }
+}
+
+static void fb_refresh1(FBDevice *fb_dev, void *opaque,
+ int x, int y, int w, int h)
+{
+ int stride = fb_dev->stride;
+ fb_refresh(opaque, fb_dev->fb_data + y * stride + x * 4, x, y, w, h,
+ stride);
+}
+
+static CharacterDevice *console_init(void)
+{
+ CharacterDevice *dev;
+ console_resize_pending = TRUE;
+ dev = mallocz(sizeof(*dev));
+ dev->write_data = console_write;
+ dev->read_data = console_read;
+ return dev;
+}
+
+typedef struct {
+ VirtMachineParams *p;
+ int ram_size;
+ char *cmdline;
+ BOOL has_network;
+ char *pwd;
+} VMStartState;
+
+static void init_vm(void *arg);
+static void init_vm_fs(void *arg);
+static void init_vm_drive(void *arg);
+
+void vm_start(const char *url, int ram_size, const char *cmdline,
+ const char *pwd, int width, int height, BOOL has_network)
+{
+ VMStartState *s;
+
+ s = mallocz(sizeof(*s));
+ s->ram_size = ram_size;
+ s->cmdline = strdup(cmdline);
+ if (pwd)
+ s->pwd = strdup(pwd);
+ global_width = width;
+ global_height = height;
+ s->has_network = has_network;
+ s->p = mallocz(sizeof(VirtMachineParams));
+ virt_machine_set_defaults(s->p);
+ virt_machine_load_config_file(s->p, url, init_vm_fs, s);
+}
+
+static void init_vm_fs(void *arg)
+{
+ VMStartState *s = arg;
+ VirtMachineParams *p = s->p;
+
+ if (p->fs_count > 0) {
+ assert(p->fs_count == 1);
+ p->tab_fs[0].fs_dev = fs_net_init(p->tab_fs[0].filename,
+ init_vm_drive, s);
+ if (s->pwd) {
+ fs_net_set_pwd(p->tab_fs[0].fs_dev, s->pwd);
+ }
+ } else {
+ init_vm_drive(s);
+ }
+}
+
+static void init_vm_drive(void *arg)
+{
+ VMStartState *s = arg;
+ VirtMachineParams *p = s->p;
+
+ if (p->drive_count > 0) {
+ assert(p->drive_count == 1);
+ p->tab_drive[0].block_dev =
+ block_device_init_http(p->tab_drive[0].filename,
+ 131072,
+ init_vm, s);
+ } else {
+ init_vm(s);
+ }
+}
+
+static void init_vm(void *arg)
+{
+ VMStartState *s = arg;
+ VirtMachine *m;
+ VirtMachineParams *p = s->p;
+ int i;
+
+ p->rtc_real_time = TRUE;
+ p->ram_size = s->ram_size << 20;
+ if (s->cmdline && s->cmdline[0] != '\0') {
+ vm_add_cmdline(s->p, s->cmdline);
+ }
+
+ if (global_width > 0 && global_height > 0) {
+ /* enable graphic output if needed */
+ if (!p->display_device)
+ p->display_device = strdup("simplefb");
+ p->width = global_width;
+ p->height = global_height;
+ } else {
+ p->console = console_init();
+ }
+
+ if (p->eth_count > 0 && !s->has_network) {
+ /* remove the interfaces */
+ for(i = 0; i < p->eth_count; i++) {
+ free(p->tab_eth[i].ifname);
+ free(p->tab_eth[i].driver);
+ }
+ p->eth_count = 0;
+ }
+
+ if (p->eth_count > 0) {
+ EthernetDevice *net;
+ int i;
+ assert(p->eth_count == 1);
+ net = mallocz(sizeof(EthernetDevice));
+ net->mac_addr[0] = 0x02;
+ for(i = 1; i < 6; i++)
+ net->mac_addr[i] = (int)(emscripten_random() * 256);
+ net->write_packet = net_recv_packet;
+ net->opaque = NULL;
+ p->tab_eth[0].net = net;
+ }
+
+ m = virt_machine_init(p);
+ global_vm = m;
+
+ virt_machine_free_config(s->p);
+
+ if (m->net) {
+ m->net->device_set_carrier(m->net, global_carrier_state);
+ }
+
+ free(s->p);
+ free(s->cmdline);
+ if (s->pwd) {
+ memset(s->pwd, 0, strlen(s->pwd));
+ free(s->pwd);
+ }
+ free(s);
+
+ emscripten_async_call(virt_machine_run, m, 0);
+}
+
+/* need to be long enough to hide the non zero delay of setTimeout(_, 0) */
+#define MAX_EXEC_TOTAL_CYCLE 3000000
+#define MAX_EXEC_CYCLE 200000
+
+#define MAX_SLEEP_TIME 10 /* in ms */
+
+void virt_machine_run(void *opaque)
+{
+ VirtMachine *m = opaque;
+ int delay, i;
+ FBDevice *fb_dev;
+
+ if (m->console_dev && virtio_console_can_write_data(m->console_dev)) {
+ uint8_t buf[128];
+ int ret, len;
+ len = virtio_console_get_write_len(m->console_dev);
+ len = min_int(len, sizeof(buf));
+ ret = m->console->read_data(m->console->opaque, buf, len);
+ if (ret > 0)
+ virtio_console_write_data(m->console_dev, buf, ret);
+ if (console_resize_pending) {
+ int w, h;
+ console_get_size(&w, &h);
+ virtio_console_resize_event(m->console_dev, w, h);
+ console_resize_pending = FALSE;
+ }
+ }
+
+ fb_dev = m->fb_dev;
+ if (fb_dev) {
+ /* refresh the display */
+ fb_dev->refresh(fb_dev, fb_refresh1, NULL);
+ }
+
+ i = 0;
+ for(;;) {
+ /* wait for an event: the only asynchronous event is the RTC timer */
+ delay = virt_machine_get_sleep_duration(m, MAX_SLEEP_TIME);
+ if (delay != 0 || i >= MAX_EXEC_TOTAL_CYCLE / MAX_EXEC_CYCLE)
+ break;
+ virt_machine_interp(m, MAX_EXEC_CYCLE);
+ i++;
+ }
+
+ if (delay == 0) {
+ emscripten_async_call(virt_machine_run, m, 0);
+ } else {
+ emscripten_async_call(virt_machine_run, m, MAX_SLEEP_TIME);
+ }
+}
+
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/json.c b/jslinux-2019-12-21/tinyemu-2019-12-21/json.c
new file mode 100644
index 0000000..d1ebe82
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/json.c
@@ -0,0 +1,464 @@
+/*
+ * Pseudo JSON parser
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "cutils.h"
+#include "json.h"
+#include "fs_utils.h"
+
+static JSONValue parse_string(const char **pp)
+{
+ char buf[4096], *q;
+ const char *p;
+ int c, h;
+
+ q = buf;
+ p = *pp;
+ p++;
+ for(;;) {
+ c = *p++;
+ if (c == '\0' || c == '\n') {
+ return json_error_new("unterminated string");
+ } else if (c == '\"') {
+ break;
+ } else if (c == '\\') {
+ c = *p++;
+ switch(c) {
+ case '\'':
+ case '\"':
+ case '\\':
+ goto add_char;
+ case 'n':
+ c = '\n';
+ goto add_char;
+ case 'r':
+ c = '\r';
+ goto add_char;
+ case 't':
+ c = '\t';
+ goto add_char;
+ case 'x':
+ h = from_hex(*p++);
+ if (h < 0)
+ return json_error_new("invalig hex digit");
+ c = h << 4;
+ h = from_hex(*p++);
+ if (h < 0)
+ return json_error_new("invalig hex digit");
+ c |= h;
+ goto add_char;
+ default:
+ return json_error_new("unknown escape code");
+ }
+ } else {
+ add_char:
+ if (q >= buf + sizeof(buf) - 1)
+ return json_error_new("string too long");
+ *q++ = c;
+ }
+ }
+ *q = '\0';
+ *pp = p;
+ return json_string_new(buf);
+}
+
+static JSONProperty *json_object_get2(JSONObject *obj, const char *name)
+{
+ JSONProperty *f;
+ int i;
+ for(i = 0; i < obj->len; i++) {
+ f = &obj->props[i];
+ if (!strcmp(f->name.u.str->data, name))
+ return f;
+ }
+ return NULL;
+}
+
+JSONValue json_object_get(JSONValue val, const char *name)
+{
+ JSONProperty *f;
+ JSONObject *obj;
+
+ if (val.type != JSON_OBJ)
+ return json_undefined_new();
+ obj = val.u.obj;
+ f = json_object_get2(obj, name);
+ if (!f)
+ return json_undefined_new();
+ return f->value;
+}
+
+int json_object_set(JSONValue val, const char *name, JSONValue prop_val)
+{
+ JSONObject *obj;
+ JSONProperty *f;
+ int new_size;
+
+ if (val.type != JSON_OBJ)
+ return -1;
+ obj = val.u.obj;
+ f = json_object_get2(obj, name);
+ if (f) {
+ json_free(f->value);
+ f->value = prop_val;
+ } else {
+ if (obj->len >= obj->size) {
+ new_size = max_int(obj->len + 1, obj->size * 3 / 2);
+ obj->props = realloc(obj->props, new_size * sizeof(JSONProperty));
+ obj->size = new_size;
+ }
+ f = &obj->props[obj->len++];
+ f->name = json_string_new(name);
+ f->value = prop_val;
+ }
+ return 0;
+}
+
+JSONValue json_array_get(JSONValue val, unsigned int idx)
+{
+ JSONArray *array;
+
+ if (val.type != JSON_ARRAY)
+ return json_undefined_new();
+ array = val.u.array;
+ if (idx < array->len) {
+ return array->tab[idx];
+ } else {
+ return json_undefined_new();
+ }
+}
+
+int json_array_set(JSONValue val, unsigned int idx, JSONValue prop_val)
+{
+ JSONArray *array;
+ int new_size;
+
+ if (val.type != JSON_ARRAY)
+ return -1;
+ array = val.u.array;
+ if (idx < array->len) {
+ json_free(array->tab[idx]);
+ array->tab[idx] = prop_val;
+ } else if (idx == array->len) {
+ if (array->len >= array->size) {
+ new_size = max_int(array->len + 1, array->size * 3 / 2);
+ array->tab = realloc(array->tab, new_size * sizeof(JSONValue));
+ array->size = new_size;
+ }
+ array->tab[array->len++] = prop_val;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+const char *json_get_str(JSONValue val)
+{
+ if (val.type != JSON_STR)
+ return NULL;
+ return val.u.str->data;
+}
+
+const char *json_get_error(JSONValue val)
+{
+ if (val.type != JSON_EXCEPTION)
+ return NULL;
+ return val.u.str->data;
+}
+
+JSONValue json_string_new2(const char *str, int len)
+{
+ JSONValue val;
+ JSONString *str1;
+
+ str1 = malloc(sizeof(JSONString) + len + 1);
+ str1->len = len;
+ memcpy(str1->data, str, len + 1);
+ val.type = JSON_STR;
+ val.u.str = str1;
+ return val;
+}
+
+JSONValue json_string_new(const char *str)
+{
+ return json_string_new2(str, strlen(str));
+}
+
+JSONValue __attribute__((format(printf, 1, 2))) json_error_new(const char *fmt, ...)
+{
+ JSONValue val;
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ val = json_string_new(buf);
+ val.type = JSON_EXCEPTION;
+ return val;
+}
+
+JSONValue json_object_new(void)
+{
+ JSONValue val;
+ JSONObject *obj;
+ obj = mallocz(sizeof(JSONObject));
+ val.type = JSON_OBJ;
+ val.u.obj = obj;
+ return val;
+}
+
+JSONValue json_array_new(void)
+{
+ JSONValue val;
+ JSONArray *array;
+ array = mallocz(sizeof(JSONArray));
+ val.type = JSON_ARRAY;
+ val.u.array = array;
+ return val;
+}
+
+void json_free(JSONValue val)
+{
+ switch(val.type) {
+ case JSON_STR:
+ case JSON_EXCEPTION:
+ free(val.u.str);
+ break;
+ case JSON_INT:
+ case JSON_BOOL:
+ case JSON_NULL:
+ case JSON_UNDEFINED:
+ break;
+ case JSON_ARRAY:
+ {
+ JSONArray *array = val.u.array;
+ int i;
+
+ for(i = 0; i < array->len; i++) {
+ json_free(array->tab[i]);
+ }
+ free(array);
+ }
+ break;
+ case JSON_OBJ:
+ {
+ JSONObject *obj = val.u.obj;
+ JSONProperty *f;
+ int i;
+
+ for(i = 0; i < obj->len; i++) {
+ f = &obj->props[i];
+ json_free(f->name);
+ json_free(f->value);
+ }
+ free(obj);
+ }
+ break;
+ default:
+ abort();
+ }
+}
+
+static void skip_spaces(const char **pp)
+{
+ const char *p;
+ p = *pp;
+ for(;;) {
+ if (isspace(*p)) {
+ p++;
+ } else if (p[0] == '/' && p[1] == '/') {
+ p += 2;
+ while (*p != '\0' && *p != '\n')
+ p++;
+ } else if (p[0] == '/' && p[1] == '*') {
+ p += 2;
+ while (*p != '\0' && (p[0] != '*' || p[1] != '/'))
+ p++;
+ if (*p != '\0')
+ p += 2;
+ } else {
+ break;
+ }
+ }
+ *pp = p;
+}
+
+static inline BOOL is_ident_first(int c)
+{
+ return (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '_' || c == '$';
+}
+
+static int parse_ident(char *buf, int buf_size, const char **pp)
+{
+ char *q;
+ const char *p;
+ p = *pp;
+ q = buf;
+ *q++ = *p++; /* first char is already tested */
+ while (is_ident_first(*p) || isdigit(*p)) {
+ if ((q - buf) >= buf_size - 1)
+ return -1;
+ *q++ = *p++;
+ }
+ *pp = p;
+ *q = '\0';
+ return 0;
+}
+
+JSONValue json_parse_value2(const char **pp)
+{
+ char buf[128];
+ const char *p;
+ JSONValue val, val1, tag;
+
+ p = *pp;
+ skip_spaces(&p);
+ if (*p == '\0') {
+ return json_error_new("unexpected end of file");
+ }
+ if (isdigit(*p)) {
+ val = json_int32_new(strtol(p, (char **)&p, 0));
+ } else if (*p == '"') {
+ val = parse_string(&p);
+ } else if (*p == '{') {
+ p++;
+ val = json_object_new();
+ for(;;) {
+ skip_spaces(&p);
+ if (*p == '}') {
+ p++;
+ break;
+ }
+ if (*p == '"') {
+ tag = parse_string(&p);
+ if (json_is_error(tag))
+ return tag;
+ } else if (is_ident_first(*p)) {
+ if (parse_ident(buf, sizeof(buf), &p) < 0)
+ goto invalid_prop;
+ tag = json_string_new(buf);
+ } else {
+ goto invalid_prop;
+ }
+ // printf("property: %s\n", json_get_str(tag));
+ if (tag.u.str->len == 0) {
+ invalid_prop:
+ return json_error_new("Invalid property name");
+ }
+ skip_spaces(&p);
+ if (*p != ':') {
+ return json_error_new("':' expected");
+ }
+ p++;
+
+ val1 = json_parse_value2(&p);
+ json_object_set(val, tag.u.str->data, val1);
+
+ skip_spaces(&p);
+ if (*p == ',') {
+ p++;
+ } else if (*p != '}') {
+ return json_error_new("expecting ',' or '}'");
+ }
+ }
+ } else if (*p == '[') {
+ int idx;
+
+ p++;
+ val = json_array_new();
+ idx = 0;
+ for(;;) {
+ skip_spaces(&p);
+ if (*p == ']') {
+ p++;
+ break;
+ }
+ val1 = json_parse_value2(&p);
+ json_array_set(val, idx++, val1);
+
+ skip_spaces(&p);
+ if (*p == ',') {
+ p++;
+ } else if (*p != ']') {
+ return json_error_new("expecting ',' or ']'");
+ }
+ }
+ } else if (is_ident_first(*p)) {
+ if (parse_ident(buf, sizeof(buf), &p) < 0)
+ goto unknown_id;
+ if (!strcmp(buf, "null")) {
+ val = json_null_new();
+ } else if (!strcmp(buf, "true")) {
+ val = json_bool_new(TRUE);
+ } else if (!strcmp(buf, "false")) {
+ val = json_bool_new(FALSE);
+ } else {
+ unknown_id:
+ return json_error_new("unknown identifier: '%s'", buf);
+ }
+ } else {
+ return json_error_new("unexpected character");
+ }
+ *pp = p;
+ return val;
+}
+
+JSONValue json_parse_value(const char *p)
+{
+ JSONValue val;
+ val = json_parse_value2(&p);
+ if (json_is_error(val))
+ return val;
+ skip_spaces(&p);
+ if (*p != '\0') {
+ json_free(val);
+ return json_error_new("unexpected characters at the end");
+ }
+ return val;
+}
+
+JSONValue json_parse_value_len(const char *p, int len)
+{
+ char *str;
+ JSONValue val;
+ str = malloc(len + 1);
+ memcpy(str, p, len);
+ str[len] = '\0';
+ val = json_parse_value(str);
+ free(str);
+ return val;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/json.h b/jslinux-2019-12-21/tinyemu-2019-12-21/json.h
new file mode 100644
index 0000000..148d050
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/json.h
@@ -0,0 +1,132 @@
+/*
+ * Pseudo JSON parser
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef JSON_H
+#define JSON_H
+
+typedef enum {
+ JSON_STR,
+ JSON_INT,
+ JSON_OBJ,
+ JSON_ARRAY,
+ JSON_BOOL,
+ JSON_NULL,
+ JSON_UNDEFINED,
+ JSON_EXCEPTION,
+} JSONTypeEnum;
+
+typedef struct {
+ int len;
+ char data[0];
+} JSONString;
+
+typedef struct JSONValue {
+ JSONTypeEnum type;
+ union {
+ JSONString *str;
+ int int32;
+ BOOL b;
+ struct JSONObject *obj;
+ struct JSONArray *array;
+ } u;
+} JSONValue;
+
+typedef struct JSONProperty {
+ JSONValue name;
+ JSONValue value;
+} JSONProperty;
+
+typedef struct JSONObject {
+ int len;
+ int size;
+ JSONProperty *props;
+} JSONObject;
+
+typedef struct JSONArray {
+ int len;
+ int size;
+ JSONValue *tab;
+} JSONArray;
+
+JSONValue json_string_new2(const char *str, int len);
+JSONValue json_string_new(const char *str);
+JSONValue __attribute__((format(printf, 1, 2))) json_error_new(const char *fmt, ...);
+void json_free(JSONValue val);
+
+JSONValue json_object_new(void);
+JSONValue json_object_get(JSONValue val, const char *name);
+int json_object_set(JSONValue val, const char *name, JSONValue prop_val);
+
+JSONValue json_array_new(void);
+JSONValue json_array_get(JSONValue val, unsigned int idx);
+int json_array_set(JSONValue val, unsigned int idx, JSONValue prop_val);
+
+static inline BOOL json_is_error(JSONValue val)
+{
+ return val.type == JSON_EXCEPTION;
+}
+
+static inline BOOL json_is_undefined(JSONValue val)
+{
+ return val.type == JSON_UNDEFINED;
+}
+
+static inline JSONValue json_undefined_new(void)
+{
+ JSONValue val;
+ val.type = JSON_UNDEFINED;
+ val.u.int32 = 0;
+ return val;
+}
+
+static inline JSONValue json_null_new(void)
+{
+ JSONValue val;
+ val.type = JSON_NULL;
+ val.u.int32 = 0;
+ return val;
+}
+
+static inline JSONValue json_int32_new(int v)
+{
+ JSONValue val;
+ val.type = JSON_INT;
+ val.u.int32 = v;
+ return val;
+}
+
+static inline JSONValue json_bool_new(BOOL v)
+{
+ JSONValue val;
+ val.type = JSON_BOOL;
+ val.u.b = v;
+ return val;
+}
+
+const char *json_get_str(JSONValue val);
+const char *json_get_error(JSONValue val);
+
+JSONValue json_parse_value(const char *p);
+JSONValue json_parse_value_len(const char *p, int len);
+
+#endif /* JSON_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/list.h b/jslinux-2019-12-21/tinyemu-2019-12-21/list.h
new file mode 100644
index 0000000..3fbea04
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/list.h
@@ -0,0 +1,94 @@
+/*
+ * Linux klist like system
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef LIST_H
+#define LIST_H
+
+struct list_head {
+ struct list_head *prev;
+ struct list_head *next;
+};
+
+/* return the pointer of type 'type *' containing 'el' as field 'member' */
+#define list_entry(el, type, member) \
+ ((type *)((uint8_t *)(el) - offsetof(type, member)))
+
+static inline void init_list_head(struct list_head *head)
+{
+ head->prev = head;
+ head->next = head;
+}
+
+/* insert 'el' between 'prev' and 'next' */
+static inline void __list_add(struct list_head *el,
+ struct list_head *prev, struct list_head *next)
+{
+ prev->next = el;
+ el->prev = prev;
+ el->next = next;
+ next->prev = el;
+}
+
+/* add 'el' at the head of the list 'head' (= after element head) */
+static inline void list_add(struct list_head *el, struct list_head *head)
+{
+ __list_add(el, head, head->next);
+}
+
+/* add 'el' at the end of the list 'head' (= before element head) */
+static inline void list_add_tail(struct list_head *el, struct list_head *head)
+{
+ __list_add(el, head->prev, head);
+}
+
+static inline void list_del(struct list_head *el)
+{
+ struct list_head *prev, *next;
+ prev = el->prev;
+ next = el->next;
+ prev->next = next;
+ next->prev = prev;
+ el->prev = NULL; /* fail safe */
+ el->next = NULL; /* fail safe */
+}
+
+static inline int list_empty(struct list_head *el)
+{
+ return el->next == el;
+}
+
+#define list_for_each(el, head) \
+ for(el = (head)->next; el != (head); el = el->next)
+
+#define list_for_each_safe(el, el1, head) \
+ for(el = (head)->next, el1 = el->next; el != (head); \
+ el = el1, el1 = el->next)
+
+#define list_for_each_prev(el, head) \
+ for(el = (head)->prev; el != (head); el = el->prev)
+
+#define list_for_each_prev_safe(el, el1, head) \
+ for(el = (head)->prev, el1 = el->prev; el != (head); \
+ el = el1, el1 = el->prev)
+
+#endif /* LIST_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/machine.c b/jslinux-2019-12-21/tinyemu-2019-12-21/machine.c
new file mode 100644
index 0000000..09c7940
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/machine.c
@@ -0,0 +1,640 @@
+/*
+ * VM utilities
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "virtio.h"
+#include "machine.h"
+#include "fs_utils.h"
+#ifdef CONFIG_FS_NET
+#include "fs_wget.h"
+#endif
+
+void __attribute__((format(printf, 1, 2))) vm_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+#ifdef EMSCRIPTEN
+ vprintf(fmt, ap);
+#else
+ vfprintf(stderr, fmt, ap);
+#endif
+ va_end(ap);
+}
+
+int vm_get_int(JSONValue obj, const char *name, int *pval)
+{
+ JSONValue val;
+ val = json_object_get(obj, name);
+ if (json_is_undefined(val)) {
+ vm_error("expecting '%s' property\n", name);
+ return -1;
+ }
+ if (val.type != JSON_INT) {
+ vm_error("%s: integer expected\n", name);
+ return -1;
+ }
+ *pval = val.u.int32;
+ return 0;
+}
+
+int vm_get_int_opt(JSONValue obj, const char *name, int *pval, int def_val)
+{
+ JSONValue val;
+ val = json_object_get(obj, name);
+ if (json_is_undefined(val)) {
+ *pval = def_val;
+ return 0;
+ }
+ if (val.type != JSON_INT) {
+ vm_error("%s: integer expected\n", name);
+ return -1;
+ }
+ *pval = val.u.int32;
+ return 0;
+}
+
+static int vm_get_str2(JSONValue obj, const char *name, const char **pstr,
+ BOOL is_opt)
+{
+ JSONValue val;
+ val = json_object_get(obj, name);
+ if (json_is_undefined(val)) {
+ if (is_opt) {
+ *pstr = NULL;
+ return 0;
+ } else {
+ vm_error("expecting '%s' property\n", name);
+ return -1;
+ }
+ }
+ if (val.type != JSON_STR) {
+ vm_error("%s: string expected\n", name);
+ return -1;
+ }
+ *pstr = val.u.str->data;
+ return 0;
+}
+
+static int vm_get_str(JSONValue obj, const char *name, const char **pstr)
+{
+ return vm_get_str2(obj, name, pstr, FALSE);
+}
+
+static int vm_get_str_opt(JSONValue obj, const char *name, const char **pstr)
+{
+ return vm_get_str2(obj, name, pstr, TRUE);
+}
+
+static char *strdup_null(const char *str)
+{
+ if (!str)
+ return NULL;
+ else
+ return strdup(str);
+}
+
+/* currently only for "TZ" */
+static char *cmdline_subst(const char *cmdline)
+{
+ DynBuf dbuf;
+ const char *p;
+ char var_name[32], *q, buf[32];
+
+ dbuf_init(&dbuf);
+ p = cmdline;
+ while (*p != '\0') {
+ if (p[0] == '$' && p[1] == '{') {
+ p += 2;
+ q = var_name;
+ while (*p != '\0' && *p != '}') {
+ if ((q - var_name) < sizeof(var_name) - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ if (*p == '}')
+ p++;
+ if (!strcmp(var_name, "TZ")) {
+ time_t ti;
+ struct tm tm;
+ int n, sg;
+ /* get the offset to UTC */
+ time(&ti);
+ localtime_r(&ti, &tm);
+ n = tm.tm_gmtoff / 60;
+ sg = '-';
+ if (n < 0) {
+ sg = '+';
+ n = -n;
+ }
+ snprintf(buf, sizeof(buf), "UTC%c%02d:%02d",
+ sg, n / 60, n % 60);
+ dbuf_putstr(&dbuf, buf);
+ }
+ } else {
+ dbuf_putc(&dbuf, *p++);
+ }
+ }
+ dbuf_putc(&dbuf, 0);
+ return (char *)dbuf.buf;
+}
+
+static BOOL find_name(const char *name, const char *name_list)
+{
+ size_t len;
+ const char *p, *r;
+
+ p = name_list;
+ for(;;) {
+ r = strchr(p, ',');
+ if (!r) {
+ if (!strcmp(name, p))
+ return TRUE;
+ break;
+ } else {
+ len = r - p;
+ if (len == strlen(name) && !memcmp(name, p, len))
+ return TRUE;
+ p = r + 1;
+ }
+ }
+ return FALSE;
+}
+
+static const VirtMachineClass *virt_machine_list[] = {
+#if defined(EMSCRIPTEN)
+ /* only a single machine in the EMSCRIPTEN target */
+#ifndef CONFIG_X86EMU
+ &riscv_machine_class,
+#endif
+#else
+ &riscv_machine_class,
+#endif /* !EMSCRIPTEN */
+#ifdef CONFIG_X86EMU
+ &pc_machine_class,
+#endif
+ NULL,
+};
+
+static const VirtMachineClass *virt_machine_find_class(const char *machine_name)
+{
+ const VirtMachineClass *vmc, **pvmc;
+
+ for(pvmc = virt_machine_list; *pvmc != NULL; pvmc++) {
+ vmc = *pvmc;
+ if (find_name(machine_name, vmc->machine_names))
+ return vmc;
+ }
+ return NULL;
+}
+
+static int virt_machine_parse_config(VirtMachineParams *p,
+ char *config_file_str, int len)
+{
+ int version, val;
+ const char *tag_name, *str;
+ char buf1[256];
+ JSONValue cfg, obj, el;
+
+ cfg = json_parse_value_len(config_file_str, len);
+ if (json_is_error(cfg)) {
+ vm_error("error: %s\n", json_get_error(cfg));
+ json_free(cfg);
+ return -1;
+ }
+
+ if (vm_get_int(cfg, "version", &version) < 0)
+ goto tag_fail;
+ if (version != VM_CONFIG_VERSION) {
+ if (version > VM_CONFIG_VERSION) {
+ vm_error("The emulator is too old to run this VM: please upgrade\n");
+ return -1;
+ } else {
+ vm_error("The VM configuration file is too old for this emulator version: please upgrade the VM configuration file\n");
+ return -1;
+ }
+ }
+
+ if (vm_get_str(cfg, "machine", &str) < 0)
+ goto tag_fail;
+ p->machine_name = strdup(str);
+ p->vmc = virt_machine_find_class(p->machine_name);
+ if (!p->vmc) {
+ vm_error("Unknown machine name: %s\n", p->machine_name);
+ goto tag_fail;
+ }
+ p->vmc->virt_machine_set_defaults(p);
+
+ tag_name = "memory_size";
+ if (vm_get_int(cfg, tag_name, &val) < 0)
+ goto tag_fail;
+ p->ram_size = (uint64_t)val << 20;
+
+ tag_name = "bios";
+ if (vm_get_str_opt(cfg, tag_name, &str) < 0)
+ goto tag_fail;
+ if (str) {
+ p->files[VM_FILE_BIOS].filename = strdup(str);
+ }
+
+ tag_name = "kernel";
+ if (vm_get_str_opt(cfg, tag_name, &str) < 0)
+ goto tag_fail;
+ if (str) {
+ p->files[VM_FILE_KERNEL].filename = strdup(str);
+ }
+
+ tag_name = "initrd";
+ if (vm_get_str_opt(cfg, tag_name, &str) < 0)
+ goto tag_fail;
+ if (str) {
+ p->files[VM_FILE_INITRD].filename = strdup(str);
+ }
+
+ if (vm_get_str_opt(cfg, "cmdline", &str) < 0)
+ goto tag_fail;
+ if (str) {
+ p->cmdline = cmdline_subst(str);
+ }
+
+ for(;;) {
+ snprintf(buf1, sizeof(buf1), "drive%d", p->drive_count);
+ obj = json_object_get(cfg, buf1);
+ if (json_is_undefined(obj))
+ break;
+ if (p->drive_count >= MAX_DRIVE_DEVICE) {
+ vm_error("Too many drives\n");
+ return -1;
+ }
+ if (vm_get_str(obj, "file", &str) < 0)
+ goto tag_fail;
+ p->tab_drive[p->drive_count].filename = strdup(str);
+ if (vm_get_str_opt(obj, "device", &str) < 0)
+ goto tag_fail;
+ p->tab_drive[p->drive_count].device = strdup_null(str);
+ p->drive_count++;
+ }
+
+ for(;;) {
+ snprintf(buf1, sizeof(buf1), "fs%d", p->fs_count);
+ obj = json_object_get(cfg, buf1);
+ if (json_is_undefined(obj))
+ break;
+ if (p->fs_count >= MAX_DRIVE_DEVICE) {
+ vm_error("Too many filesystems\n");
+ return -1;
+ }
+ if (vm_get_str(obj, "file", &str) < 0)
+ goto tag_fail;
+ p->tab_fs[p->fs_count].filename = strdup(str);
+ if (vm_get_str_opt(obj, "tag", &str) < 0)
+ goto tag_fail;
+ if (!str) {
+ if (p->fs_count == 0)
+ strcpy(buf1, "/dev/root");
+ else
+ snprintf(buf1, sizeof(buf1), "/dev/root%d", p->fs_count);
+ str = buf1;
+ }
+ p->tab_fs[p->fs_count].tag = strdup(str);
+ p->fs_count++;
+ }
+
+ for(;;) {
+ snprintf(buf1, sizeof(buf1), "eth%d", p->eth_count);
+ obj = json_object_get(cfg, buf1);
+ if (json_is_undefined(obj))
+ break;
+ if (p->eth_count >= MAX_ETH_DEVICE) {
+ vm_error("Too many ethernet interfaces\n");
+ return -1;
+ }
+ if (vm_get_str(obj, "driver", &str) < 0)
+ goto tag_fail;
+ p->tab_eth[p->eth_count].driver = strdup(str);
+ if (!strcmp(str, "tap")) {
+ if (vm_get_str(obj, "ifname", &str) < 0)
+ goto tag_fail;
+ p->tab_eth[p->eth_count].ifname = strdup(str);
+ }
+ p->eth_count++;
+ }
+
+ p->display_device = NULL;
+ obj = json_object_get(cfg, "display0");
+ if (!json_is_undefined(obj)) {
+ if (vm_get_str(obj, "device", &str) < 0)
+ goto tag_fail;
+ p->display_device = strdup(str);
+ if (vm_get_int(obj, "width", &p->width) < 0)
+ goto tag_fail;
+ if (vm_get_int(obj, "height", &p->height) < 0)
+ goto tag_fail;
+ if (vm_get_str_opt(obj, "vga_bios", &str) < 0)
+ goto tag_fail;
+ if (str) {
+ p->files[VM_FILE_VGA_BIOS].filename = strdup(str);
+ }
+ }
+
+ if (vm_get_str_opt(cfg, "input_device", &str) < 0)
+ goto tag_fail;
+ p->input_device = strdup_null(str);
+
+ if (vm_get_str_opt(cfg, "accel", &str) < 0)
+ goto tag_fail;
+ if (str) {
+ if (!strcmp(str, "none")) {
+ p->accel_enable = FALSE;
+ } else if (!strcmp(str, "auto")) {
+ p->accel_enable = TRUE;
+ } else {
+ vm_error("unsupported 'accel' config: %s\n", str);
+ return -1;
+ }
+ }
+
+ tag_name = "rtc_local_time";
+ el = json_object_get(cfg, tag_name);
+ if (!json_is_undefined(el)) {
+ if (el.type != JSON_BOOL) {
+ vm_error("%s: boolean expected\n", tag_name);
+ goto tag_fail;
+ }
+ p->rtc_local_time = el.u.b;
+ }
+
+ json_free(cfg);
+ return 0;
+ tag_fail:
+ json_free(cfg);
+ return -1;
+}
+
+typedef void FSLoadFileCB(void *opaque, uint8_t *buf, int buf_len);
+
+typedef struct {
+ VirtMachineParams *vm_params;
+ void (*start_cb)(void *opaque);
+ void *opaque;
+
+ FSLoadFileCB *file_load_cb;
+ void *file_load_opaque;
+ int file_index;
+} VMConfigLoadState;
+
+static void config_file_loaded(void *opaque, uint8_t *buf, int buf_len);
+static void config_additional_file_load(VMConfigLoadState *s);
+static void config_additional_file_load_cb(void *opaque,
+ uint8_t *buf, int buf_len);
+
+/* XXX: win32, URL */
+char *get_file_path(const char *base_filename, const char *filename)
+{
+ int len, len1;
+ char *fname, *p;
+
+ if (!base_filename)
+ goto done;
+ if (strchr(filename, ':'))
+ goto done; /* full URL */
+ if (filename[0] == '/')
+ goto done;
+ p = strrchr(base_filename, '/');
+ if (!p) {
+ done:
+ return strdup(filename);
+ }
+ len = p + 1 - base_filename;
+ len1 = strlen(filename);
+ fname = malloc(len + len1 + 1);
+ memcpy(fname, base_filename, len);
+ memcpy(fname + len, filename, len1 + 1);
+ return fname;
+}
+
+
+#ifdef EMSCRIPTEN
+static int load_file(uint8_t **pbuf, const char *filename)
+{
+ abort();
+}
+#else
+/* return -1 if error. */
+static int load_file(uint8_t **pbuf, const char *filename)
+{
+ FILE *f;
+ int size;
+ uint8_t *buf;
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ perror(filename);
+ exit(1);
+ }
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ buf = malloc(size);
+ if (fread(buf, 1, size, f) != size) {
+ fprintf(stderr, "%s: read error\n", filename);
+ exit(1);
+ }
+ fclose(f);
+ *pbuf = buf;
+ return size;
+}
+#endif
+
+#ifdef CONFIG_FS_NET
+static void config_load_file_cb(void *opaque, int err, void *data, size_t size)
+{
+ VMConfigLoadState *s = opaque;
+
+ // printf("err=%d data=%p size=%ld\n", err, data, size);
+ if (err < 0) {
+ vm_error("Error %d while loading file\n", -err);
+ exit(1);
+ }
+ s->file_load_cb(s->file_load_opaque, data, size);
+}
+#endif
+
+static void config_load_file(VMConfigLoadState *s, const char *filename,
+ FSLoadFileCB *cb, void *opaque)
+{
+ // printf("loading %s\n", filename);
+#ifdef CONFIG_FS_NET
+ if (is_url(filename)) {
+ s->file_load_cb = cb;
+ s->file_load_opaque = opaque;
+ fs_wget(filename, NULL, NULL, s, config_load_file_cb, TRUE);
+ } else
+#endif
+ {
+ uint8_t *buf;
+ int size;
+ size = load_file(&buf, filename);
+ cb(opaque, buf, size);
+ free(buf);
+ }
+}
+
+void virt_machine_load_config_file(VirtMachineParams *p,
+ const char *filename,
+ void (*start_cb)(void *opaque),
+ void *opaque)
+{
+ VMConfigLoadState *s;
+
+ s = mallocz(sizeof(*s));
+ s->vm_params = p;
+ s->start_cb = start_cb;
+ s->opaque = opaque;
+ p->cfg_filename = strdup(filename);
+
+ config_load_file(s, filename, config_file_loaded, s);
+}
+
+static void config_file_loaded(void *opaque, uint8_t *buf, int buf_len)
+{
+ VMConfigLoadState *s = opaque;
+ VirtMachineParams *p = s->vm_params;
+
+ if (virt_machine_parse_config(p, (char *)buf, buf_len) < 0)
+ exit(1);
+
+ /* load the additional files */
+ s->file_index = 0;
+ config_additional_file_load(s);
+}
+
+static void config_additional_file_load(VMConfigLoadState *s)
+{
+ VirtMachineParams *p = s->vm_params;
+ while (s->file_index < VM_FILE_COUNT &&
+ p->files[s->file_index].filename == NULL) {
+ s->file_index++;
+ }
+ if (s->file_index == VM_FILE_COUNT) {
+ if (s->start_cb)
+ s->start_cb(s->opaque);
+ free(s);
+ } else {
+ char *fname;
+
+ fname = get_file_path(p->cfg_filename,
+ p->files[s->file_index].filename);
+ config_load_file(s, fname,
+ config_additional_file_load_cb, s);
+ free(fname);
+ }
+}
+
+static void config_additional_file_load_cb(void *opaque,
+ uint8_t *buf, int buf_len)
+{
+ VMConfigLoadState *s = opaque;
+ VirtMachineParams *p = s->vm_params;
+
+ p->files[s->file_index].buf = malloc(buf_len);
+ memcpy(p->files[s->file_index].buf, buf, buf_len);
+ p->files[s->file_index].len = buf_len;
+
+ /* load the next files */
+ s->file_index++;
+ config_additional_file_load(s);
+}
+
+void vm_add_cmdline(VirtMachineParams *p, const char *cmdline)
+{
+ char *new_cmdline, *old_cmdline;
+ if (cmdline[0] == '!') {
+ new_cmdline = strdup(cmdline + 1);
+ } else {
+ old_cmdline = p->cmdline;
+ if (!old_cmdline)
+ old_cmdline = "";
+ new_cmdline = malloc(strlen(old_cmdline) + 1 + strlen(cmdline) + 1);
+ strcpy(new_cmdline, old_cmdline);
+ strcat(new_cmdline, " ");
+ strcat(new_cmdline, cmdline);
+ }
+ free(p->cmdline);
+ p->cmdline = new_cmdline;
+}
+
+void virt_machine_free_config(VirtMachineParams *p)
+{
+ int i;
+
+ free(p->machine_name);
+ free(p->cmdline);
+ for(i = 0; i < VM_FILE_COUNT; i++) {
+ free(p->files[i].filename);
+ free(p->files[i].buf);
+ }
+ for(i = 0; i < p->drive_count; i++) {
+ free(p->tab_drive[i].filename);
+ free(p->tab_drive[i].device);
+ }
+ for(i = 0; i < p->fs_count; i++) {
+ free(p->tab_fs[i].filename);
+ free(p->tab_fs[i].tag);
+ }
+ for(i = 0; i < p->eth_count; i++) {
+ free(p->tab_eth[i].driver);
+ free(p->tab_eth[i].ifname);
+ }
+ free(p->input_device);
+ free(p->display_device);
+ free(p->cfg_filename);
+}
+
+VirtMachine *virt_machine_init(const VirtMachineParams *p)
+{
+ const VirtMachineClass *vmc = p->vmc;
+ return vmc->virt_machine_init(p);
+}
+
+void virt_machine_set_defaults(VirtMachineParams *p)
+{
+ memset(p, 0, sizeof(*p));
+}
+
+void virt_machine_end(VirtMachine *s)
+{
+ s->vmc->virt_machine_end(s);
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/machine.h b/jslinux-2019-12-21/tinyemu-2019-12-21/machine.h
new file mode 100644
index 0000000..76217ee
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/machine.h
@@ -0,0 +1,196 @@
+/*
+ * VM definitions
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "json.h"
+
+typedef struct FBDevice FBDevice;
+
+typedef void SimpleFBDrawFunc(FBDevice *fb_dev, void *opaque,
+ int x, int y, int w, int h);
+
+struct FBDevice {
+ /* the following is set by the device */
+ int width;
+ int height;
+ int stride; /* current stride in bytes */
+ uint8_t *fb_data; /* current pointer to the pixel data */
+ int fb_size; /* frame buffer memory size (info only) */
+ void *device_opaque;
+ void (*refresh)(struct FBDevice *fb_dev,
+ SimpleFBDrawFunc *redraw_func, void *opaque);
+};
+
+#define MAX_DRIVE_DEVICE 4
+#define MAX_FS_DEVICE 4
+#define MAX_ETH_DEVICE 1
+
+#define VM_CONFIG_VERSION 1
+
+typedef enum {
+ VM_FILE_BIOS,
+ VM_FILE_VGA_BIOS,
+ VM_FILE_KERNEL,
+ VM_FILE_INITRD,
+
+ VM_FILE_COUNT,
+} VMFileTypeEnum;
+
+typedef struct {
+ char *filename;
+ uint8_t *buf;
+ int len;
+} VMFileEntry;
+
+typedef struct {
+ char *device;
+ char *filename;
+ BlockDevice *block_dev;
+} VMDriveEntry;
+
+typedef struct {
+ char *device;
+ char *tag; /* 9p mount tag */
+ char *filename;
+ FSDevice *fs_dev;
+} VMFSEntry;
+
+typedef struct {
+ char *driver;
+ char *ifname;
+ EthernetDevice *net;
+} VMEthEntry;
+
+typedef struct VirtMachineClass VirtMachineClass;
+
+typedef struct {
+ char *cfg_filename;
+ const VirtMachineClass *vmc;
+ char *machine_name;
+ uint64_t ram_size;
+ BOOL rtc_real_time;
+ BOOL rtc_local_time;
+ char *display_device; /* NULL means no display */
+ int width, height; /* graphic width & height */
+ CharacterDevice *console;
+ VMDriveEntry tab_drive[MAX_DRIVE_DEVICE];
+ int drive_count;
+ VMFSEntry tab_fs[MAX_FS_DEVICE];
+ int fs_count;
+ VMEthEntry tab_eth[MAX_ETH_DEVICE];
+ int eth_count;
+
+ char *cmdline; /* bios or kernel command line */
+ BOOL accel_enable; /* enable acceleration (KVM) */
+ char *input_device; /* NULL means no input */
+
+ /* kernel, bios and other auxiliary files */
+ VMFileEntry files[VM_FILE_COUNT];
+} VirtMachineParams;
+
+typedef struct VirtMachine {
+ const VirtMachineClass *vmc;
+ /* network */
+ EthernetDevice *net;
+ /* console */
+ VIRTIODevice *console_dev;
+ CharacterDevice *console;
+ /* graphics */
+ FBDevice *fb_dev;
+} VirtMachine;
+
+struct VirtMachineClass {
+ const char *machine_names;
+ void (*virt_machine_set_defaults)(VirtMachineParams *p);
+ VirtMachine *(*virt_machine_init)(const VirtMachineParams *p);
+ void (*virt_machine_end)(VirtMachine *s);
+ int (*virt_machine_get_sleep_duration)(VirtMachine *s, int delay);
+ void (*virt_machine_interp)(VirtMachine *s, int max_exec_cycle);
+ BOOL (*vm_mouse_is_absolute)(VirtMachine *s);
+ void (*vm_send_mouse_event)(VirtMachine *s1, int dx, int dy, int dz,
+ unsigned int buttons);
+ void (*vm_send_key_event)(VirtMachine *s1, BOOL is_down, uint16_t key_code);
+};
+
+extern const VirtMachineClass riscv_machine_class;
+extern const VirtMachineClass pc_machine_class;
+
+void __attribute__((format(printf, 1, 2))) vm_error(const char *fmt, ...);
+int vm_get_int(JSONValue obj, const char *name, int *pval);
+int vm_get_int_opt(JSONValue obj, const char *name, int *pval, int def_val);
+
+void virt_machine_set_defaults(VirtMachineParams *p);
+void virt_machine_load_config_file(VirtMachineParams *p,
+ const char *filename,
+ void (*start_cb)(void *opaque),
+ void *opaque);
+void vm_add_cmdline(VirtMachineParams *p, const char *cmdline);
+char *get_file_path(const char *base_filename, const char *filename);
+void virt_machine_free_config(VirtMachineParams *p);
+VirtMachine *virt_machine_init(const VirtMachineParams *p);
+void virt_machine_end(VirtMachine *s);
+static inline int virt_machine_get_sleep_duration(VirtMachine *s, int delay)
+{
+ return s->vmc->virt_machine_get_sleep_duration(s, delay);
+}
+static inline void virt_machine_interp(VirtMachine *s, int max_exec_cycle)
+{
+ s->vmc->virt_machine_interp(s, max_exec_cycle);
+}
+static inline BOOL vm_mouse_is_absolute(VirtMachine *s)
+{
+ return s->vmc->vm_mouse_is_absolute(s);
+}
+static inline void vm_send_mouse_event(VirtMachine *s1, int dx, int dy, int dz,
+ unsigned int buttons)
+{
+ s1->vmc->vm_send_mouse_event(s1, dx, dy, dz, buttons);
+}
+static inline void vm_send_key_event(VirtMachine *s1, BOOL is_down, uint16_t key_code)
+{
+ s1->vmc->vm_send_key_event(s1, is_down, key_code);
+}
+
+/* gui */
+void sdl_refresh(VirtMachine *m);
+void sdl_init(int width, int height);
+
+/* simplefb.c */
+typedef struct SimpleFBState SimpleFBState;
+SimpleFBState *simplefb_init(PhysMemoryMap *map, uint64_t phys_addr,
+ FBDevice *fb_dev, int width, int height);
+void simplefb_refresh(FBDevice *fb_dev,
+ SimpleFBDrawFunc *redraw_func, void *opaque,
+ PhysMemoryRange *mem_range,
+ int fb_page_count);
+
+/* vga.c */
+typedef struct VGAState VGAState;
+VGAState *pci_vga_init(PCIBus *bus, FBDevice *fb_dev,
+ int width, int height,
+ const uint8_t *vga_rom_buf, int vga_rom_size);
+
+/* block_net.c */
+BlockDevice *block_device_init_http(const char *url,
+ int max_cache_size_kb,
+ void (*start_cb)(void *opaque),
+ void *start_opaque);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/netinit.sh b/jslinux-2019-12-21/tinyemu-2019-12-21/netinit.sh
new file mode 100755
index 0000000..c2dadf9
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/netinit.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# RISCVEMU Ethernet bridge and NAT configuration (run with sudo)
+#
+# Copyright (c) 2017 Fabrice Bellard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+# host network interface connected to Internet (change it)
+internet_ifname="enp0s20f0u1"
+
+# setup bridge interface
+ip link add br0 type bridge
+# create and add tap0 interface to bridge
+ip tuntap add dev tap0 mode tap user $USER
+ip link set tap0 master br0
+
+ip link set dev br0 up
+ip link set dev tap0 up
+ifconfig br0 192.168.3.1
+
+# setup NAT to access to Internet
+echo 1 > /proc/sys/net/ipv4/ip_forward
+# delete forwarding reject rule if present
+#iptables -D FORWARD 1
+iptables -t nat -A POSTROUTING -o $internet_ifname -j MASQUERADE
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/pci.c b/jslinux-2019-12-21/tinyemu-2019-12-21/pci.c
new file mode 100644
index 0000000..d37b737
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/pci.c
@@ -0,0 +1,588 @@
+/*
+ * Simple PCI bus driver
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "cutils.h"
+#include "pci.h"
+
+//#define DEBUG_CONFIG
+
+typedef struct {
+ uint32_t size; /* 0 means no mapping defined */
+ uint8_t type;
+ uint8_t enabled; /* true if mapping is enabled */
+ void *opaque;
+ PCIBarSetFunc *bar_set;
+} PCIIORegion;
+
+struct PCIDevice {
+ PCIBus *bus;
+ uint8_t devfn;
+ IRQSignal irq[4];
+ uint8_t config[256];
+ uint8_t next_cap_offset; /* offset of the next capability */
+ char *name; /* for debug only */
+ PCIIORegion io_regions[PCI_NUM_REGIONS];
+};
+
+struct PCIBus {
+ int bus_num;
+ PCIDevice *device[256];
+ PhysMemoryMap *mem_map;
+ PhysMemoryMap *port_map;
+ uint32_t irq_state[4][8]; /* one bit per device */
+ IRQSignal irq[4];
+};
+
+static int bus_map_irq(PCIDevice *d, int irq_num)
+{
+ int slot_addend;
+ slot_addend = (d->devfn >> 3) - 1;
+ return (irq_num + slot_addend) & 3;
+}
+
+static void pci_device_set_irq(void *opaque, int irq_num, int level)
+{
+ PCIDevice *d = opaque;
+ PCIBus *b = d->bus;
+ uint32_t mask;
+ int i, irq_level;
+
+ // printf("%s: pci_device_seq_irq: %d %d\n", d->name, irq_num, level);
+ irq_num = bus_map_irq(d, irq_num);
+ mask = 1 << (d->devfn & 0x1f);
+ if (level)
+ b->irq_state[irq_num][d->devfn >> 5] |= mask;
+ else
+ b->irq_state[irq_num][d->devfn >> 5] &= ~mask;
+
+ /* compute the IRQ state */
+ mask = 0;
+ for(i = 0; i < 8; i++)
+ mask |= b->irq_state[irq_num][i];
+ irq_level = (mask != 0);
+ set_irq(&b->irq[irq_num], irq_level);
+}
+
+static int devfn_alloc(PCIBus *b)
+{
+ int devfn;
+ for(devfn = 0; devfn < 256; devfn += 8) {
+ if (!b->device[devfn])
+ return devfn;
+ }
+ return -1;
+}
+
+/* devfn < 0 means to allocate it */
+PCIDevice *pci_register_device(PCIBus *b, const char *name, int devfn,
+ uint16_t vendor_id, uint16_t device_id,
+ uint8_t revision, uint16_t class_id)
+{
+ PCIDevice *d;
+ int i;
+
+ if (devfn < 0) {
+ devfn = devfn_alloc(b);
+ if (devfn < 0)
+ return NULL;
+ }
+ if (b->device[devfn])
+ return NULL;
+
+ d = mallocz(sizeof(PCIDevice));
+ d->bus = b;
+ d->name = strdup(name);
+ d->devfn = devfn;
+
+ put_le16(d->config + 0x00, vendor_id);
+ put_le16(d->config + 0x02, device_id);
+ d->config[0x08] = revision;
+ put_le16(d->config + 0x0a, class_id);
+ d->config[0x0e] = 0x00; /* header type */
+ d->next_cap_offset = 0x40;
+
+ for(i = 0; i < 4; i++)
+ irq_init(&d->irq[i], pci_device_set_irq, d, i);
+ b->device[devfn] = d;
+
+ return d;
+}
+
+IRQSignal *pci_device_get_irq(PCIDevice *d, unsigned int irq_num)
+{
+ assert(irq_num < 4);
+ return &d->irq[irq_num];
+}
+
+static uint32_t pci_device_config_read(PCIDevice *d, uint32_t addr,
+ int size_log2)
+{
+ uint32_t val;
+ switch(size_log2) {
+ case 0:
+ val = *(uint8_t *)(d->config + addr);
+ break;
+ case 1:
+ /* Note: may be unaligned */
+ if (addr <= 0xfe)
+ val = get_le16(d->config + addr);
+ else
+ val = *(uint8_t *)(d->config + addr);
+ break;
+ case 2:
+ /* always aligned */
+ val = get_le32(d->config + addr);
+ break;
+ default:
+ abort();
+ }
+#ifdef DEBUG_CONFIG
+ printf("pci_config_read: dev=%s addr=0x%02x val=0x%x s=%d\n",
+ d->name, addr, val, 1 << size_log2);
+#endif
+ return val;
+}
+
+PhysMemoryMap *pci_device_get_mem_map(PCIDevice *d)
+{
+ return d->bus->mem_map;
+}
+
+PhysMemoryMap *pci_device_get_port_map(PCIDevice *d)
+{
+ return d->bus->port_map;
+}
+
+void pci_register_bar(PCIDevice *d, unsigned int bar_num,
+ uint32_t size, int type,
+ void *opaque, PCIBarSetFunc *bar_set)
+{
+ PCIIORegion *r;
+ uint32_t val, config_addr;
+
+ assert(bar_num < PCI_NUM_REGIONS);
+ assert((size & (size - 1)) == 0); /* power of two */
+ assert(size >= 4);
+ r = &d->io_regions[bar_num];
+ assert(r->size == 0);
+ r->size = size;
+ r->type = type;
+ r->enabled = FALSE;
+ r->opaque = opaque;
+ r->bar_set = bar_set;
+ /* set the config value */
+ val = 0;
+ if (bar_num == PCI_ROM_SLOT) {
+ config_addr = 0x30;
+ } else {
+ val |= r->type;
+ config_addr = 0x10 + 4 * bar_num;
+ }
+ put_le32(&d->config[config_addr], val);
+}
+
+static void pci_update_mappings(PCIDevice *d)
+{
+ int cmd, i, offset;
+ uint32_t new_addr;
+ BOOL new_enabled;
+ PCIIORegion *r;
+
+ cmd = get_le16(&d->config[PCI_COMMAND]);
+
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[i];
+ if (i == PCI_ROM_SLOT) {
+ offset = 0x30;
+ } else {
+ offset = 0x10 + i * 4;
+ }
+ new_addr = get_le32(&d->config[offset]);
+ new_enabled = FALSE;
+ if (r->size != 0) {
+ if ((r->type & PCI_ADDRESS_SPACE_IO) &&
+ (cmd & PCI_COMMAND_IO)) {
+ new_enabled = TRUE;
+ } else {
+ if (cmd & PCI_COMMAND_MEMORY) {
+ if (i == PCI_ROM_SLOT) {
+ new_enabled = (new_addr & 1);
+ } else {
+ new_enabled = TRUE;
+ }
+ }
+ }
+ }
+ if (new_enabled) {
+ /* new address */
+ new_addr = get_le32(&d->config[offset]) & ~(r->size - 1);
+ r->bar_set(r->opaque, i, new_addr, TRUE);
+ r->enabled = TRUE;
+ } else if (r->enabled) {
+ r->bar_set(r->opaque, i, 0, FALSE);
+ r->enabled = FALSE;
+ }
+ }
+}
+
+/* return != 0 if write is not handled */
+static int pci_write_bar(PCIDevice *d, uint32_t addr,
+ uint32_t val)
+{
+ PCIIORegion *r;
+ int reg;
+
+ if (addr == 0x30)
+ reg = PCI_ROM_SLOT;
+ else
+ reg = (addr - 0x10) >> 2;
+ // printf("%s: write bar addr=%x data=%x\n", d->name, addr, val);
+ r = &d->io_regions[reg];
+ if (r->size == 0)
+ return -1;
+ if (reg == PCI_ROM_SLOT) {
+ val = val & ((~(r->size - 1)) | 1);
+ } else {
+ val = (val & ~(r->size - 1)) | r->type;
+ }
+ put_le32(d->config + addr, val);
+ pci_update_mappings(d);
+ return 0;
+}
+
+static void pci_device_config_write8(PCIDevice *d, uint32_t addr,
+ uint32_t data)
+{
+ int can_write;
+
+ if (addr == PCI_STATUS || addr == (PCI_STATUS + 1)) {
+ /* write 1 reset bits */
+ d->config[addr] &= ~data;
+ return;
+ }
+
+ switch(d->config[0x0e]) {
+ case 0x00:
+ case 0x80:
+ switch(addr) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0e:
+ case 0x10 ... 0x27: /* base */
+ case 0x30 ... 0x33: /* rom */
+ case 0x3d:
+ can_write = 0;
+ break;
+ default:
+ can_write = 1;
+ break;
+ }
+ break;
+ default:
+ case 0x01:
+ switch(addr) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0e:
+ case 0x38 ... 0x3b: /* rom */
+ case 0x3d:
+ can_write = 0;
+ break;
+ default:
+ can_write = 1;
+ break;
+ }
+ break;
+ }
+ if (can_write)
+ d->config[addr] = data;
+}
+
+
+static void pci_device_config_write(PCIDevice *d, uint32_t addr,
+ uint32_t data, int size_log2)
+{
+ int size, i;
+ uint32_t addr1;
+
+#ifdef DEBUG_CONFIG
+ printf("pci_config_write: dev=%s addr=0x%02x val=0x%x s=%d\n",
+ d->name, addr, data, 1 << size_log2);
+#endif
+ if (size_log2 == 2 &&
+ ((addr >= 0x10 && addr < 0x10 + 4 * 6) ||
+ addr == 0x30)) {
+ if (pci_write_bar(d, addr, data) == 0)
+ return;
+ }
+ size = 1 << size_log2;
+ for(i = 0; i < size; i++) {
+ addr1 = addr + i;
+ if (addr1 <= 0xff) {
+ pci_device_config_write8(d, addr1, (data >> (i * 8)) & 0xff);
+ }
+ }
+ if (PCI_COMMAND >= addr && PCI_COMMAND < addr + size) {
+ pci_update_mappings(d);
+ }
+}
+
+
+static void pci_data_write(PCIBus *s, uint32_t addr,
+ uint32_t data, int size_log2)
+{
+ PCIDevice *d;
+ int bus_num, devfn, config_addr;
+
+ bus_num = (addr >> 16) & 0xff;
+ if (bus_num != s->bus_num)
+ return;
+ devfn = (addr >> 8) & 0xff;
+ d = s->device[devfn];
+ if (!d)
+ return;
+ config_addr = addr & 0xff;
+ pci_device_config_write(d, config_addr, data, size_log2);
+}
+
+static const uint32_t val_ones[3] = { 0xff, 0xffff, 0xffffffff };
+
+static uint32_t pci_data_read(PCIBus *s, uint32_t addr, int size_log2)
+{
+ PCIDevice *d;
+ int bus_num, devfn, config_addr;
+
+ bus_num = (addr >> 16) & 0xff;
+ if (bus_num != s->bus_num)
+ return val_ones[size_log2];
+ devfn = (addr >> 8) & 0xff;
+ d = s->device[devfn];
+ if (!d)
+ return val_ones[size_log2];
+ config_addr = addr & 0xff;
+ return pci_device_config_read(d, config_addr, size_log2);
+}
+
+/* warning: only valid for one DEVIO page. Return NULL if no memory at
+ the given address */
+uint8_t *pci_device_get_dma_ptr(PCIDevice *d, uint64_t addr, BOOL is_rw)
+{
+ return phys_mem_get_ram_ptr(d->bus->mem_map, addr, is_rw);
+}
+
+void pci_device_set_config8(PCIDevice *d, uint8_t addr, uint8_t val)
+{
+ d->config[addr] = val;
+}
+
+void pci_device_set_config16(PCIDevice *d, uint8_t addr, uint16_t val)
+{
+ put_le16(&d->config[addr], val);
+}
+
+int pci_device_get_devfn(PCIDevice *d)
+{
+ return d->devfn;
+}
+
+/* return the offset of the capability or < 0 if error. */
+int pci_add_capability(PCIDevice *d, const uint8_t *buf, int size)
+{
+ int offset;
+
+ offset = d->next_cap_offset;
+ if ((offset + size) > 256)
+ return -1;
+ d->next_cap_offset += size;
+ d->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST;
+ memcpy(d->config + offset, buf, size);
+ d->config[offset + 1] = d->config[PCI_CAPABILITY_LIST];
+ d->config[PCI_CAPABILITY_LIST] = offset;
+ return offset;
+}
+
+/* i440FX host bridge */
+
+struct I440FXState {
+ PCIBus *pci_bus;
+ PCIDevice *pci_dev;
+ PCIDevice *piix3_dev;
+ uint32_t config_reg;
+ uint8_t pic_irq_state[16];
+ IRQSignal *pic_irqs; /* 16 irqs */
+};
+
+static void i440fx_write_addr(void *opaque, uint32_t offset,
+ uint32_t data, int size_log2)
+{
+ I440FXState *s = opaque;
+ s->config_reg = data;
+}
+
+static uint32_t i440fx_read_addr(void *opaque, uint32_t offset, int size_log2)
+{
+ I440FXState *s = opaque;
+ return s->config_reg;
+}
+
+static void i440fx_write_data(void *opaque, uint32_t offset,
+ uint32_t data, int size_log2)
+{
+ I440FXState *s = opaque;
+ if (s->config_reg & 0x80000000) {
+ if (size_log2 == 2) {
+ /* it is simpler to assume 32 bit config accesses are
+ always aligned */
+ pci_data_write(s->pci_bus, s->config_reg & ~3, data, size_log2);
+ } else {
+ pci_data_write(s->pci_bus, s->config_reg | offset, data, size_log2);
+ }
+ }
+}
+
+static uint32_t i440fx_read_data(void *opaque, uint32_t offset, int size_log2)
+{
+ I440FXState *s = opaque;
+ if (!(s->config_reg & 0x80000000))
+ return val_ones[size_log2];
+ if (size_log2 == 2) {
+ /* it is simpler to assume 32 bit config accesses are
+ always aligned */
+ return pci_data_read(s->pci_bus, s->config_reg & ~3, size_log2);
+ } else {
+ return pci_data_read(s->pci_bus, s->config_reg | offset, size_log2);
+ }
+}
+
+static void i440fx_set_irq(void *opaque, int irq_num, int irq_level)
+{
+ I440FXState *s = opaque;
+ PCIDevice *hd = s->piix3_dev;
+ int pic_irq;
+
+ /* map to the PIC irq (different IRQs can be mapped to the same
+ PIC irq) */
+ hd->config[0x60 + irq_num] &= ~0x80;
+ pic_irq = hd->config[0x60 + irq_num];
+ if (pic_irq < 16) {
+ if (irq_level)
+ s->pic_irq_state[pic_irq] |= 1 << irq_num;
+ else
+ s->pic_irq_state[pic_irq] &= ~(1 << irq_num);
+ set_irq(&s->pic_irqs[pic_irq], (s->pic_irq_state[pic_irq] != 0));
+ }
+}
+
+I440FXState *i440fx_init(PCIBus **pbus, int *ppiix3_devfn,
+ PhysMemoryMap *mem_map, PhysMemoryMap *port_map,
+ IRQSignal *pic_irqs)
+{
+ I440FXState *s;
+ PCIBus *b;
+ PCIDevice *d;
+ int i;
+
+ s = mallocz(sizeof(*s));
+
+ b = mallocz(sizeof(PCIBus));
+ b->bus_num = 0;
+ b->mem_map = mem_map;
+ b->port_map = port_map;
+
+ s->pic_irqs = pic_irqs;
+ for(i = 0; i < 4; i++) {
+ irq_init(&b->irq[i], i440fx_set_irq, s, i);
+ }
+
+ cpu_register_device(port_map, 0xcf8, 1, s, i440fx_read_addr, i440fx_write_addr,
+ DEVIO_SIZE32);
+ cpu_register_device(port_map, 0xcfc, 4, s, i440fx_read_data, i440fx_write_data,
+ DEVIO_SIZE8 | DEVIO_SIZE16 | DEVIO_SIZE32);
+ d = pci_register_device(b, "i440FX", 0, 0x8086, 0x1237, 0x02, 0x0600);
+ put_le16(&d->config[PCI_SUBSYSTEM_VENDOR_ID], 0x1af4); /* Red Hat, Inc. */
+ put_le16(&d->config[PCI_SUBSYSTEM_ID], 0x1100); /* QEMU virtual machine */
+
+ s->pci_dev = d;
+ s->pci_bus = b;
+
+ s->piix3_dev = pci_register_device(b, "PIIX3", 8, 0x8086, 0x7000,
+ 0x00, 0x0601);
+ pci_device_set_config8(s->piix3_dev, 0x0e, 0x80); /* header type */
+
+ *pbus = b;
+ *ppiix3_devfn = s->piix3_dev->devfn;
+ return s;
+}
+
+/* in case no BIOS is used, map the interrupts. */
+void i440fx_map_interrupts(I440FXState *s, uint8_t *elcr,
+ const uint8_t *pci_irqs)
+{
+ PCIBus *b = s->pci_bus;
+ PCIDevice *d, *hd;
+ int irq_num, pic_irq, devfn, i;
+
+ /* set a default PCI IRQ mapping to PIC IRQs */
+ hd = s->piix3_dev;
+
+ elcr[0] = 0;
+ elcr[1] = 0;
+ for(i = 0; i < 4; i++) {
+ irq_num = pci_irqs[i];
+ hd->config[0x60 + i] = irq_num;
+ elcr[irq_num >> 3] |= (1 << (irq_num & 7));
+ }
+
+ for(devfn = 0; devfn < 256; devfn++) {
+ d = b->device[devfn];
+ if (!d)
+ continue;
+ if (d->config[PCI_INTERRUPT_PIN]) {
+ irq_num = 0;
+ irq_num = bus_map_irq(d, irq_num);
+ pic_irq = hd->config[0x60 + irq_num];
+ if (pic_irq < 16) {
+ d->config[PCI_INTERRUPT_LINE] = pic_irq;
+ }
+ }
+ }
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/pci.h b/jslinux-2019-12-21/tinyemu-2019-12-21/pci.h
new file mode 100644
index 0000000..39d6efe
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/pci.h
@@ -0,0 +1,81 @@
+/*
+ * Simple PCI bus driver
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef PCI_H
+#define PCI_H
+
+#include "iomem.h"
+
+typedef struct PCIBus PCIBus;
+typedef struct PCIDevice PCIDevice;
+
+/* bar type */
+#define PCI_ADDRESS_SPACE_MEM 0x00
+#define PCI_ADDRESS_SPACE_IO 0x01
+#define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08
+
+#define PCI_ROM_SLOT 6
+#define PCI_NUM_REGIONS 7
+
+/* PCI config addresses */
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO (1 << 0)
+#define PCI_COMMAND_MEMORY (1 << 1)
+#define PCI_STATUS 0x06 /* 16 bits */
+#define PCI_STATUS_CAP_LIST (1 << 4)
+#define PCI_CLASS_PROG 0x09
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2c /* 16 bits */
+#define PCI_SUBSYSTEM_ID 0x2e /* 16 bits */
+#define PCI_CAPABILITY_LIST 0x34 /* 8 bits */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+
+typedef void PCIBarSetFunc(void *opaque, int bar_num, uint32_t addr,
+ BOOL enabled);
+
+PCIDevice *pci_register_device(PCIBus *b, const char *name, int devfn,
+ uint16_t vendor_id, uint16_t device_id,
+ uint8_t revision, uint16_t class_id);
+PhysMemoryMap *pci_device_get_mem_map(PCIDevice *d);
+PhysMemoryMap *pci_device_get_port_map(PCIDevice *d);
+void pci_register_bar(PCIDevice *d, unsigned int bar_num,
+ uint32_t size, int type,
+ void *opaque, PCIBarSetFunc *bar_set);
+IRQSignal *pci_device_get_irq(PCIDevice *d, unsigned int irq_num);
+uint8_t *pci_device_get_dma_ptr(PCIDevice *d, uint64_t addr, BOOL is_rw);
+void pci_device_set_config8(PCIDevice *d, uint8_t addr, uint8_t val);
+void pci_device_set_config16(PCIDevice *d, uint8_t addr, uint16_t val);
+int pci_device_get_devfn(PCIDevice *d);
+int pci_add_capability(PCIDevice *d, const uint8_t *buf, int size);
+
+typedef struct I440FXState I440FXState;
+
+I440FXState *i440fx_init(PCIBus **pbus, int *ppiix3_devfn,
+ PhysMemoryMap *mem_map, PhysMemoryMap *port_map,
+ IRQSignal *pic_irqs);
+void i440fx_map_interrupts(I440FXState *s, uint8_t *elcr,
+ const uint8_t *pci_irqs);
+
+#endif /* PCI_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/pckbd.c b/jslinux-2019-12-21/tinyemu-2019-12-21/pckbd.c
new file mode 100644
index 0000000..e1c73d7
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/pckbd.c
@@ -0,0 +1,342 @@
+/*
+ * QEMU PC keyboard emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "ps2.h"
+#include "virtio.h"
+#include "machine.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/* Keyboard Controller Commands */
+#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
+#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
+#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
+#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
+#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
+#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
+#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
+#define KBD_CCMD_WRITE_OBUF 0xD2
+#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
+ initiated by the auxiliary device */
+#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
+#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
+#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
+#define KBD_CCMD_RESET 0xFE
+
+/* Status Register Bits */
+#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
+#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
+#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
+#define KBD_STAT_PERR 0x80 /* Parity error */
+
+/* Controller Mode Register Bits */
+#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
+#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 0x04 /* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
+#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
+#define KBD_MODE_RFU 0x80
+
+#define KBD_PENDING_KBD 1
+#define KBD_PENDING_AUX 2
+
+struct KBDState {
+ uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
+ uint8_t status;
+ uint8_t mode;
+ /* Bitmask of devices with data available. */
+ uint8_t pending;
+ PS2KbdState *kbd;
+ PS2MouseState *mouse;
+
+ IRQSignal *irq_kbd;
+ IRQSignal *irq_mouse;
+};
+
+static void qemu_system_reset_request(void)
+{
+ printf("system_reset_request\n");
+ exit(1);
+ /* XXX */
+}
+
+static void ioport_set_a20(int val)
+{
+}
+
+static int ioport_get_a20(void)
+{
+ return 1;
+}
+
+/* update irq and KBD_STAT_[MOUSE_]OBF */
+/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
+ incorrect, but it avoids having to simulate exact delays */
+static void kbd_update_irq(KBDState *s)
+{
+ int irq_kbd_level, irq_mouse_level;
+
+ irq_kbd_level = 0;
+ irq_mouse_level = 0;
+ s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
+ if (s->pending) {
+ s->status |= KBD_STAT_OBF;
+ /* kbd data takes priority over aux data. */
+ if (s->pending == KBD_PENDING_AUX) {
+ s->status |= KBD_STAT_MOUSE_OBF;
+ if (s->mode & KBD_MODE_MOUSE_INT)
+ irq_mouse_level = 1;
+ } else {
+ if ((s->mode & KBD_MODE_KBD_INT) &&
+ !(s->mode & KBD_MODE_DISABLE_KBD))
+ irq_kbd_level = 1;
+ }
+ }
+ set_irq(s->irq_kbd, irq_kbd_level);
+ set_irq(s->irq_mouse, irq_mouse_level);
+}
+
+static void kbd_update_kbd_irq(void *opaque, int level)
+{
+ KBDState *s = (KBDState *)opaque;
+
+ if (level)
+ s->pending |= KBD_PENDING_KBD;
+ else
+ s->pending &= ~KBD_PENDING_KBD;
+ kbd_update_irq(s);
+}
+
+static void kbd_update_aux_irq(void *opaque, int level)
+{
+ KBDState *s = (KBDState *)opaque;
+
+ if (level)
+ s->pending |= KBD_PENDING_AUX;
+ else
+ s->pending &= ~KBD_PENDING_AUX;
+ kbd_update_irq(s);
+}
+
+static uint32_t kbd_read_status(void *opaque, uint32_t addr, int size_log2)
+{
+ KBDState *s = opaque;
+ int val;
+ val = s->status;
+#if defined(DEBUG_KBD)
+ printf("kbd: read status=0x%02x\n", val);
+#endif
+ return val;
+}
+
+static void kbd_queue(KBDState *s, int b, int aux)
+{
+ if (aux)
+ ps2_queue(s->mouse, b);
+ else
+ ps2_queue(s->kbd, b);
+}
+
+static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val,
+ int size_log2)
+{
+ KBDState *s = opaque;
+
+#if defined(DEBUG_KBD)
+ printf("kbd: write cmd=0x%02x\n", val);
+#endif
+ switch(val) {
+ case KBD_CCMD_READ_MODE:
+ kbd_queue(s, s->mode, 1);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ case KBD_CCMD_WRITE_OBUF:
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ case KBD_CCMD_WRITE_MOUSE:
+ case KBD_CCMD_WRITE_OUTPORT:
+ s->write_cmd = val;
+ break;
+ case KBD_CCMD_MOUSE_DISABLE:
+ s->mode |= KBD_MODE_DISABLE_MOUSE;
+ break;
+ case KBD_CCMD_MOUSE_ENABLE:
+ s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ break;
+ case KBD_CCMD_TEST_MOUSE:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_SELF_TEST:
+ s->status |= KBD_STAT_SELFTEST;
+ kbd_queue(s, 0x55, 0);
+ break;
+ case KBD_CCMD_KBD_TEST:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_KBD_DISABLE:
+ s->mode |= KBD_MODE_DISABLE_KBD;
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_KBD_ENABLE:
+ s->mode &= ~KBD_MODE_DISABLE_KBD;
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_READ_INPORT:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_READ_OUTPORT:
+ /* XXX: check that */
+ val = 0x01 | (ioport_get_a20() << 1);
+ if (s->status & KBD_STAT_OBF)
+ val |= 0x10;
+ if (s->status & KBD_STAT_MOUSE_OBF)
+ val |= 0x20;
+ kbd_queue(s, val, 0);
+ break;
+ case KBD_CCMD_ENABLE_A20:
+ ioport_set_a20(1);
+ break;
+ case KBD_CCMD_DISABLE_A20:
+ ioport_set_a20(0);
+ break;
+ case KBD_CCMD_RESET:
+ qemu_system_reset_request();
+ break;
+ case 0xff:
+ /* ignore that - I don't know what is its use */
+ break;
+ default:
+ fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
+ break;
+ }
+}
+
+static uint32_t kbd_read_data(void *opaque, uint32_t addr, int size_log2)
+{
+ KBDState *s = opaque;
+ uint32_t val;
+ if (s->pending == KBD_PENDING_AUX)
+ val = ps2_read_data(s->mouse);
+ else
+ val = ps2_read_data(s->kbd);
+#ifdef DEBUG_KBD
+ printf("kbd: read data=0x%02x\n", val);
+#endif
+ return val;
+}
+
+static void kbd_write_data(void *opaque, uint32_t addr, uint32_t val, int size_log2)
+{
+ KBDState *s = opaque;
+
+#ifdef DEBUG_KBD
+ printf("kbd: write data=0x%02x\n", val);
+#endif
+
+ switch(s->write_cmd) {
+ case 0:
+ ps2_write_keyboard(s->kbd, val);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ s->mode = val;
+ ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
+ /* ??? */
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_WRITE_OBUF:
+ kbd_queue(s, val, 0);
+ break;
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ kbd_queue(s, val, 1);
+ break;
+ case KBD_CCMD_WRITE_OUTPORT:
+ ioport_set_a20((val >> 1) & 1);
+ if (!(val & 1)) {
+ qemu_system_reset_request();
+ }
+ break;
+ case KBD_CCMD_WRITE_MOUSE:
+ ps2_write_mouse(s->mouse, val);
+ break;
+ default:
+ break;
+ }
+ s->write_cmd = 0;
+}
+
+static void kbd_reset(void *opaque)
+{
+ KBDState *s = opaque;
+
+ s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
+ s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
+}
+
+KBDState *i8042_init(PS2KbdState **pkbd,
+ PS2MouseState **pmouse,
+ PhysMemoryMap *port_map,
+ IRQSignal *kbd_irq, IRQSignal *mouse_irq, uint32_t io_base)
+{
+ KBDState *s;
+
+ s = mallocz(sizeof(*s));
+
+ s->irq_kbd = kbd_irq;
+ s->irq_mouse = mouse_irq;
+
+ kbd_reset(s);
+ cpu_register_device(port_map, io_base, 1, s, kbd_read_data, kbd_write_data,
+ DEVIO_SIZE8);
+ cpu_register_device(port_map, io_base + 4, 1, s, kbd_read_status, kbd_write_command,
+ DEVIO_SIZE8);
+
+ s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+ s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+
+ *pkbd = s->kbd;
+ *pmouse = s->mouse;
+ return s;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/ps2.c b/jslinux-2019-12-21/tinyemu-2019-12-21/ps2.c
new file mode 100644
index 0000000..c787568
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/ps2.c
@@ -0,0 +1,489 @@
+/*
+ * QEMU PS/2 keyboard/mouse emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "ps2.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
+#define KBD_CMD_ECHO 0xEE
+#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
+#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
+#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
+#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
+#define KBD_CMD_RESET 0xFF /* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ACK 0xFA /* Command ACK */
+#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
+#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
+#define AUX_SET_RES 0xE8 /* Set resolution */
+#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
+#define AUX_SET_STREAM 0xEA /* Set stream mode */
+#define AUX_POLL 0xEB /* Poll */
+#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
+#define AUX_SET_WRAP 0xEE /* Set wrap mode */
+#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
+#define AUX_GET_TYPE 0xF2 /* Get type */
+#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
+#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
+#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
+#define AUX_SET_DEFAULT 0xF6
+#define AUX_RESET 0xFF /* Reset aux device */
+#define AUX_ACK 0xFA /* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE 0x40
+#define MOUSE_STATUS_ENABLED 0x20
+#define MOUSE_STATUS_SCALE21 0x10
+
+#define PS2_QUEUE_SIZE 256
+
+typedef struct {
+ uint8_t data[PS2_QUEUE_SIZE];
+ int rptr, wptr, count;
+} PS2Queue;
+
+typedef struct {
+ PS2Queue queue;
+ int32_t write_cmd;
+ void (*update_irq)(void *, int);
+ void *update_arg;
+} PS2State;
+
+struct PS2KbdState {
+ PS2State common;
+ int scan_enabled;
+ /* Qemu uses translated PC scancodes internally. To avoid multiple
+ conversions we do the translation (if any) in the PS/2 emulation
+ not the keyboard controller. */
+ int translate;
+};
+
+struct PS2MouseState {
+ PS2State common;
+ uint8_t mouse_status;
+ uint8_t mouse_resolution;
+ uint8_t mouse_sample_rate;
+ uint8_t mouse_wrap;
+ uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
+ uint8_t mouse_detect_state;
+ int mouse_dx; /* current values, needed for 'poll' mode */
+ int mouse_dy;
+ int mouse_dz;
+ uint8_t mouse_buttons;
+};
+
+void ps2_queue(void *opaque, int b)
+{
+ PS2State *s = (PS2State *)opaque;
+ PS2Queue *q = &s->queue;
+
+ if (q->count >= PS2_QUEUE_SIZE)
+ return;
+ q->data[q->wptr] = b;
+ if (++q->wptr == PS2_QUEUE_SIZE)
+ q->wptr = 0;
+ q->count++;
+ s->update_irq(s->update_arg, 1);
+}
+
+#define INPUT_MAKE_KEY_MIN 96
+#define INPUT_MAKE_KEY_MAX 127
+
+static const uint8_t linux_input_to_keycode_set1[INPUT_MAKE_KEY_MAX - INPUT_MAKE_KEY_MIN + 1] = {
+ 0x1c, 0x1d, 0x35, 0x00, 0x38, 0x00, 0x47, 0x48,
+ 0x49, 0x4b, 0x4d, 0x4f, 0x50, 0x51, 0x52, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5c, 0x5d,
+};
+
+/* keycode is a Linux input layer keycode. We only support the PS/2
+ keycode set 1 */
+void ps2_put_keycode(PS2KbdState *s, BOOL is_down, int keycode)
+{
+ if (keycode >= INPUT_MAKE_KEY_MIN) {
+ if (keycode > INPUT_MAKE_KEY_MAX)
+ return;
+ keycode = linux_input_to_keycode_set1[keycode - INPUT_MAKE_KEY_MIN];
+ if (keycode == 0)
+ return;
+ ps2_queue(&s->common, 0xe0);
+ }
+ ps2_queue(&s->common, keycode | ((!is_down) << 7));
+}
+
+uint32_t ps2_read_data(void *opaque)
+{
+ PS2State *s = (PS2State *)opaque;
+ PS2Queue *q;
+ int val, index;
+
+ q = &s->queue;
+ if (q->count == 0) {
+ /* NOTE: if no data left, we return the last keyboard one
+ (needed for EMM386) */
+ /* XXX: need a timer to do things correctly */
+ index = q->rptr - 1;
+ if (index < 0)
+ index = PS2_QUEUE_SIZE - 1;
+ val = q->data[index];
+ } else {
+ val = q->data[q->rptr];
+ if (++q->rptr == PS2_QUEUE_SIZE)
+ q->rptr = 0;
+ q->count--;
+ /* reading deasserts IRQ */
+ s->update_irq(s->update_arg, 0);
+ /* reassert IRQs if data left */
+ s->update_irq(s->update_arg, q->count != 0);
+ }
+ return val;
+}
+
+static void ps2_reset_keyboard(PS2KbdState *s)
+{
+ s->scan_enabled = 1;
+}
+
+void ps2_write_keyboard(void *opaque, int val)
+{
+ PS2KbdState *s = (PS2KbdState *)opaque;
+
+ switch(s->common.write_cmd) {
+ default:
+ case -1:
+ switch(val) {
+ case 0x00:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case 0x05:
+ ps2_queue(&s->common, KBD_REPLY_RESEND);
+ break;
+ case KBD_CMD_GET_ID:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ ps2_queue(&s->common, 0xab);
+ ps2_queue(&s->common, 0x83);
+ break;
+ case KBD_CMD_ECHO:
+ ps2_queue(&s->common, KBD_CMD_ECHO);
+ break;
+ case KBD_CMD_ENABLE:
+ s->scan_enabled = 1;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_SET_LEDS:
+ case KBD_CMD_SET_RATE:
+ s->common.write_cmd = val;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET_DISABLE:
+ ps2_reset_keyboard(s);
+ s->scan_enabled = 0;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET_ENABLE:
+ ps2_reset_keyboard(s);
+ s->scan_enabled = 1;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET:
+ ps2_reset_keyboard(s);
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ ps2_queue(&s->common, KBD_REPLY_POR);
+ break;
+ default:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ }
+ break;
+ case KBD_CMD_SET_LEDS:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ s->common.write_cmd = -1;
+ break;
+ case KBD_CMD_SET_RATE:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ s->common.write_cmd = -1;
+ break;
+ }
+}
+
+/* Set the scancode translation mode.
+ 0 = raw scancodes.
+ 1 = translated scancodes (used by qemu internally). */
+
+void ps2_keyboard_set_translation(void *opaque, int mode)
+{
+ PS2KbdState *s = (PS2KbdState *)opaque;
+ s->translate = mode;
+}
+
+static void ps2_mouse_send_packet(PS2MouseState *s)
+{
+ unsigned int b;
+ int dx1, dy1, dz1;
+
+ dx1 = s->mouse_dx;
+ dy1 = s->mouse_dy;
+ dz1 = s->mouse_dz;
+ /* XXX: increase range to 8 bits ? */
+ if (dx1 > 127)
+ dx1 = 127;
+ else if (dx1 < -127)
+ dx1 = -127;
+ if (dy1 > 127)
+ dy1 = 127;
+ else if (dy1 < -127)
+ dy1 = -127;
+ b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
+ ps2_queue(&s->common, b);
+ ps2_queue(&s->common, dx1 & 0xff);
+ ps2_queue(&s->common, dy1 & 0xff);
+ /* extra byte for IMPS/2 or IMEX */
+ switch(s->mouse_type) {
+ default:
+ break;
+ case 3:
+ if (dz1 > 127)
+ dz1 = 127;
+ else if (dz1 < -127)
+ dz1 = -127;
+ ps2_queue(&s->common, dz1 & 0xff);
+ break;
+ case 4:
+ if (dz1 > 7)
+ dz1 = 7;
+ else if (dz1 < -7)
+ dz1 = -7;
+ b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
+ ps2_queue(&s->common, b);
+ break;
+ }
+
+ /* update deltas */
+ s->mouse_dx -= dx1;
+ s->mouse_dy -= dy1;
+ s->mouse_dz -= dz1;
+}
+
+void ps2_mouse_event(PS2MouseState *s,
+ int dx, int dy, int dz, int buttons_state)
+{
+ /* check if deltas are recorded when disabled */
+ if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
+ return;
+
+ s->mouse_dx += dx;
+ s->mouse_dy -= dy;
+ s->mouse_dz += dz;
+ /* XXX: SDL sometimes generates nul events: we delete them */
+ if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
+ s->mouse_buttons == buttons_state)
+ return;
+ s->mouse_buttons = buttons_state;
+
+ if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
+ (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
+ for(;;) {
+ /* if not remote, send event. Multiple events are sent if
+ too big deltas */
+ ps2_mouse_send_packet(s);
+ if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
+ break;
+ }
+ }
+}
+
+void ps2_write_mouse(void *opaque, int val)
+{
+ PS2MouseState *s = (PS2MouseState *)opaque;
+#ifdef DEBUG_MOUSE
+ printf("kbd: write mouse 0x%02x\n", val);
+#endif
+ switch(s->common.write_cmd) {
+ default:
+ case -1:
+ /* mouse command */
+ if (s->mouse_wrap) {
+ if (val == AUX_RESET_WRAP) {
+ s->mouse_wrap = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ return;
+ } else if (val != AUX_RESET) {
+ ps2_queue(&s->common, val);
+ return;
+ }
+ }
+ switch(val) {
+ case AUX_SET_SCALE11:
+ s->mouse_status &= ~MOUSE_STATUS_SCALE21;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_SCALE21:
+ s->mouse_status |= MOUSE_STATUS_SCALE21;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_STREAM:
+ s->mouse_status &= ~MOUSE_STATUS_REMOTE;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_WRAP:
+ s->mouse_wrap = 1;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_REMOTE:
+ s->mouse_status |= MOUSE_STATUS_REMOTE;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_GET_TYPE:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, s->mouse_type);
+ break;
+ case AUX_SET_RES:
+ case AUX_SET_SAMPLE:
+ s->common.write_cmd = val;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_GET_SCALE:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, s->mouse_status);
+ ps2_queue(&s->common, s->mouse_resolution);
+ ps2_queue(&s->common, s->mouse_sample_rate);
+ break;
+ case AUX_POLL:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_mouse_send_packet(s);
+ break;
+ case AUX_ENABLE_DEV:
+ s->mouse_status |= MOUSE_STATUS_ENABLED;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_DISABLE_DEV:
+ s->mouse_status &= ~MOUSE_STATUS_ENABLED;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_DEFAULT:
+ s->mouse_sample_rate = 100;
+ s->mouse_resolution = 2;
+ s->mouse_status = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_RESET:
+ s->mouse_sample_rate = 100;
+ s->mouse_resolution = 2;
+ s->mouse_status = 0;
+ s->mouse_type = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, 0xaa);
+ ps2_queue(&s->common, s->mouse_type);
+ break;
+ default:
+ break;
+ }
+ break;
+ case AUX_SET_SAMPLE:
+ s->mouse_sample_rate = val;
+ /* detect IMPS/2 or IMEX */
+ switch(s->mouse_detect_state) {
+ default:
+ case 0:
+ if (val == 200)
+ s->mouse_detect_state = 1;
+ break;
+ case 1:
+ if (val == 100)
+ s->mouse_detect_state = 2;
+ else if (val == 200)
+ s->mouse_detect_state = 3;
+ else
+ s->mouse_detect_state = 0;
+ break;
+ case 2:
+ if (val == 80)
+ s->mouse_type = 3; /* IMPS/2 */
+ s->mouse_detect_state = 0;
+ break;
+ case 3:
+ if (val == 80)
+ s->mouse_type = 4; /* IMEX */
+ s->mouse_detect_state = 0;
+ break;
+ }
+ ps2_queue(&s->common, AUX_ACK);
+ s->common.write_cmd = -1;
+ break;
+ case AUX_SET_RES:
+ s->mouse_resolution = val;
+ ps2_queue(&s->common, AUX_ACK);
+ s->common.write_cmd = -1;
+ break;
+ }
+}
+
+static void ps2_reset(void *opaque)
+{
+ PS2State *s = (PS2State *)opaque;
+ PS2Queue *q;
+ s->write_cmd = -1;
+ q = &s->queue;
+ q->rptr = 0;
+ q->wptr = 0;
+ q->count = 0;
+}
+
+PS2KbdState *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
+{
+ PS2KbdState *s = (PS2KbdState *)mallocz(sizeof(PS2KbdState));
+
+ s->common.update_irq = update_irq;
+ s->common.update_arg = update_arg;
+ ps2_reset(&s->common);
+ return s;
+}
+
+PS2MouseState *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
+{
+ PS2MouseState *s = (PS2MouseState *)mallocz(sizeof(PS2MouseState));
+
+ s->common.update_irq = update_irq;
+ s->common.update_arg = update_arg;
+ ps2_reset(&s->common);
+ return s;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/ps2.h b/jslinux-2019-12-21/tinyemu-2019-12-21/ps2.h
new file mode 100644
index 0000000..47bcfe9
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/ps2.h
@@ -0,0 +1,34 @@
+/* ps2.c */
+typedef struct PS2MouseState PS2MouseState;
+typedef struct PS2KbdState PS2KbdState;
+
+PS2KbdState *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
+PS2MouseState *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
+void ps2_write_mouse(void *, int val);
+void ps2_write_keyboard(void *, int val);
+uint32_t ps2_read_data(void *);
+void ps2_queue(void *, int b);
+void ps2_keyboard_set_translation(void *opaque, int mode);
+
+void ps2_put_keycode(PS2KbdState *s, BOOL is_down, int keycode);
+void ps2_mouse_event(PS2MouseState *s,
+ int dx, int dy, int dz, int buttons_state);
+
+/* vmmouse.c */
+typedef struct VMMouseState VMMouseState;
+
+VMMouseState *vmmouse_init(PS2MouseState *ps2_mouse);
+BOOL vmmouse_is_absolute(VMMouseState *s);
+void vmmouse_send_mouse_event(VMMouseState *s, int x, int y, int dz,
+ int buttons);
+void vmmouse_handler(VMMouseState *s, uint32_t *regs);
+
+/* pckbd.c */
+
+typedef struct KBDState KBDState;
+
+KBDState *i8042_init(PS2KbdState **pkbd,
+ PS2MouseState **pmouse,
+ PhysMemoryMap *port_map,
+ IRQSignal *kbd_irq, IRQSignal *mouse_irq,
+ uint32_t io_base);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/readme.txt b/jslinux-2019-12-21/tinyemu-2019-12-21/readme.txt
new file mode 100644
index 0000000..ca72f7d
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/readme.txt
@@ -0,0 +1,204 @@
+TinyEMU System Emulator by Fabrice Bellard
+==========================================
+
+1) Features
+-----------
+
+- RISC-V system emulator supporting the RV128IMAFDQC base ISA (user
+ level ISA version 2.2, priviledged architecture version 1.10)
+ including:
+
+ - 32/64/128 bit integer registers
+ - 32/64/128 bit floating point instructions
+ - Compressed instructions
+ - dynamic XLEN change
+
+- x86 system emulator based on KVM
+
+- VirtIO console, network, block device, input and 9P filesystem
+
+- Graphical display with SDL
+
+- JSON configuration file
+
+- Remote HTTP block device and filesystem
+
+- small code, easy to modify, no external dependancies
+
+- Javascript demo version
+
+2) Installation
+---------------
+
+- The libraries libcurl, OpenSSL and SDL should be installed. On a Fedora
+ system you can do it with:
+
+ sudo dnf install openssl-devel libcurl-devel SDL-devel
+
+ It is possible to compile the programs without these libraries by
+ commenting CONFIG_FS_NET and/or CONFIG_SDL in the Makefile.
+
+- Edit the Makefile to disable the 128 bit target if you compile on a
+ 32 bit host (for the 128 bit RISCV target the compiler must support
+ the __int128 C extension).
+
+- Use 'make' to compile the binaries.
+
+- You can optionally install the program to '/usr/local/bin' with:
+
+ make install
+
+3) Usage
+--------
+
+3.1 Quick examples
+------------------
+
+- Use the VM images available from https://bellard.org/jslinux (no
+ need to download them):
+
+ Terminal:
+
+ ./temu https://bellard.org/jslinux/buildroot-riscv64.cfg
+
+ Graphical (with SDL):
+
+ ./temu https://bellard.org/jslinux/buildroot-x86-xwin.cfg
+
+ ./temu https://bellard.org/jslinux/win2k.cfg
+
+- Download the example RISC-V Linux image
+ (diskimage-linux-riscv-yyyy-mm-dd.tar.gz) and use it:
+
+ ./temu root-riscv64.cfg
+
+ ./temu rv128test/rv128test.cfg
+
+- Access to your local hard disk (/tmp directory) in the guest:
+
+ ./temu root_9p-riscv64.cfg
+
+then type:
+mount -t 9p /dev/root /mnt
+
+in the guest. The content of the host '/tmp' directory is visible in '/mnt'.
+
+3.2 Invocation
+--------------
+
+usage: temu [options] config_file
+options are:
+-m ram_size set the RAM size in MB
+-rw allow write access to the disk image (default=snapshot)
+-ctrlc the C-c key stops the emulator instead of being sent to the
+ emulated software
+-append cmdline append cmdline to the kernel command line
+-no-accel disable VM acceleration (KVM, x86 machine only)
+
+Console keys:
+Press C-a x to exit the emulator, C-a h to get some help.
+
+3.3 Network usage
+-----------------
+
+The easiest way is to use the "user" mode network driver. No specific
+configuration is necessary.
+
+TinyEMU also supports a "tap" network driver to redirect the network
+traffic from a VirtIO network adapter.
+
+You can look at the netinit.sh script to create the tap network
+interface and to redirect the virtual traffic to Internet thru a
+NAT. The exact configuration may depend on the Linux distribution and
+local firewall configuration.
+
+The VM configuration file must include:
+
+eth0: { driver: "tap", ifname: "tap0" }
+
+and configure the network in the guest system with:
+
+ifconfig eth0 192.168.3.2
+route add -net 0.0.0.0 gw 192.168.3.1 eth0
+
+3.4 Network filesystem
+----------------------
+
+TinyEMU supports the VirtIO 9P filesystem to access local or remote
+filesystems. For remote filesystems, it does HTTP requests to download
+the files. The protocol is compatible with the vfsync utility. In the
+"mount" command, "/dev/rootN" must be used as device name where N is
+the index of the filesystem. When N=0 it is omitted.
+
+The build_filelist tool builds the file list from a root directory. A
+simple web server is enough to serve the files.
+
+The '.preload' file gives a list of files to preload when opening a
+given file.
+
+3.5 Network block device
+------------------------
+
+TinyEMU supports an HTTP block device. The disk image is split into
+small files. Use the 'splitimg' utility to generate images. The URL of
+the JSON blk.txt file must be provided as disk image filename.
+
+4) Technical notes
+------------------
+
+4.1) 128 bit support
+
+The RISC-V specification does not define all the instruction encodings
+for the 128 bit integer and floating point operations. The missing
+ones were interpolated from the 32 and 64 ones.
+
+Unfortunately there is no RISC-V 128 bit toolchain nor OS now
+(volunteers for the Linux port ?), so rv128test.bin may be the first
+128 bit code for RISC-V !
+
+4.2) Floating point emulation
+
+The floating point emulation is bit exact and supports all the
+specified instructions for 32, 64 and 128 bit floating point
+numbers. It uses the new SoftFP library.
+
+4.3) HTIF console
+
+The standard HTIF console uses registers at variable addresses which
+are deduced by loading specific ELF symbols. TinyEMU does not rely on
+an ELF loader, so it is much simpler to use registers at fixed
+addresses (0x40008000). A small modification was made in the
+"riscv-pk" boot loader to support it. The HTIF console is only used to
+display boot messages and to power off the virtual system. The OS
+should use the VirtIO console.
+
+4.4) Javascript version
+
+The Javascript version (JSLinux) can be compiled with Makefile.js and
+emscripten. A complete precompiled and preconfigured demo is available
+in the jslinux-yyyy-mm-dd.tar.gz archive (read the readme.txt file
+inside the archive).
+
+4.5) x86 emulator
+
+A small x86 emulator is included. It is not really an emulator because
+it uses the Linux KVM API to run the x86 code at near native
+performance. The x86 emulator uses the same set of VirtIO devices as
+the RISCV emulator and is able to run many operating systems.
+
+The x86 emulator accepts a Linux kernel image (bzImage). No BIOS image
+is necessary.
+
+The x86 emulator comes from my JS/Linux project (2011) which was one
+of the first emulator running Linux fully implemented in
+Javascript. It is provided to allow easy access to the x86 images
+hosted at https://bellard.org/jslinux .
+
+
+5) License / Credits
+--------------------
+
+TinyEMU is released under the MIT license. If there is no explicit
+license in a file, the license from MIT-LICENSE.txt applies.
+
+The SLIRP library has its own license (two clause BSD license).
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu.c b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu.c
new file mode 100644
index 0000000..ba4fa4c
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu.c
@@ -0,0 +1,1377 @@
+/*
+ * RISCV CPU emulator
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "riscv_cpu.h"
+
+#ifndef MAX_XLEN
+#error MAX_XLEN must be defined
+#endif
+#ifndef CONFIG_RISCV_MAX_XLEN
+#error CONFIG_RISCV_MAX_XLEN must be defined
+#endif
+
+//#define DUMP_INVALID_MEM_ACCESS
+//#define DUMP_MMU_EXCEPTIONS
+//#define DUMP_INTERRUPTS
+//#define DUMP_INVALID_CSR
+//#define DUMP_EXCEPTIONS
+//#define DUMP_CSR
+//#define CONFIG_LOGFILE
+
+#include "riscv_cpu_priv.h"
+
+#if FLEN > 0
+#include "softfp.h"
+#endif
+
+#ifdef USE_GLOBAL_STATE
+static RISCVCPUState riscv_cpu_global_state;
+#endif
+#ifdef USE_GLOBAL_VARIABLES
+#define code_ptr s->__code_ptr
+#define code_end s->__code_end
+#define code_to_pc_addend s->__code_to_pc_addend
+#endif
+
+#ifdef CONFIG_LOGFILE
+static FILE *log_file;
+
+static void log_vprintf(const char *fmt, va_list ap)
+{
+ if (!log_file)
+ log_file = fopen("/tmp/riscemu.log", "wb");
+ vfprintf(log_file, fmt, ap);
+}
+#else
+static void log_vprintf(const char *fmt, va_list ap)
+{
+ vprintf(fmt, ap);
+}
+#endif
+
+static void __attribute__((format(printf, 1, 2), unused)) log_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ log_vprintf(fmt, ap);
+ va_end(ap);
+}
+
+#if MAX_XLEN == 128
+static void fprint_target_ulong(FILE *f, target_ulong a)
+{
+ fprintf(f, "%016" PRIx64 "%016" PRIx64, (uint64_t)(a >> 64), (uint64_t)a);
+}
+#else
+static void fprint_target_ulong(FILE *f, target_ulong a)
+{
+ fprintf(f, "%" PR_target_ulong, a);
+}
+#endif
+
+static void print_target_ulong(target_ulong a)
+{
+ fprint_target_ulong(stdout, a);
+}
+
+static char *reg_name[32] = {
+"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
+"s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
+"a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
+"s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
+};
+
+static void dump_regs(RISCVCPUState *s)
+{
+ int i, cols;
+ const char priv_str[4] = "USHM";
+ cols = 256 / MAX_XLEN;
+ printf("pc =");
+ print_target_ulong(s->pc);
+ printf(" ");
+ for(i = 1; i < 32; i++) {
+ printf("%-3s=", reg_name[i]);
+ print_target_ulong(s->reg[i]);
+ if ((i & (cols - 1)) == (cols - 1))
+ printf("\n");
+ else
+ printf(" ");
+ }
+ printf("priv=%c", priv_str[s->priv]);
+ printf(" mstatus=");
+ print_target_ulong(s->mstatus);
+ printf(" cycles=%" PRId64, s->insn_counter);
+ printf("\n");
+#if 1
+ printf(" mideleg=");
+ print_target_ulong(s->mideleg);
+ printf(" mie=");
+ print_target_ulong(s->mie);
+ printf(" mip=");
+ print_target_ulong(s->mip);
+ printf("\n");
+#endif
+}
+
+static __attribute__((unused)) void cpu_abort(RISCVCPUState *s)
+{
+ dump_regs(s);
+ abort();
+}
+
+/* addr must be aligned. Only RAM accesses are supported */
+#define PHYS_MEM_READ_WRITE(size, uint_type) \
+static __maybe_unused inline void phys_write_u ## size(RISCVCPUState *s, target_ulong addr,\
+ uint_type val) \
+{\
+ PhysMemoryRange *pr = get_phys_mem_range(s->mem_map, addr);\
+ if (!pr || !pr->is_ram)\
+ return;\
+ *(uint_type *)(pr->phys_mem + \
+ (uintptr_t)(addr - pr->addr)) = val;\
+}\
+\
+static __maybe_unused inline uint_type phys_read_u ## size(RISCVCPUState *s, target_ulong addr) \
+{\
+ PhysMemoryRange *pr = get_phys_mem_range(s->mem_map, addr);\
+ if (!pr || !pr->is_ram)\
+ return 0;\
+ return *(uint_type *)(pr->phys_mem + \
+ (uintptr_t)(addr - pr->addr)); \
+}
+
+PHYS_MEM_READ_WRITE(8, uint8_t)
+PHYS_MEM_READ_WRITE(32, uint32_t)
+PHYS_MEM_READ_WRITE(64, uint64_t)
+
+#define PTE_V_MASK (1 << 0)
+#define PTE_U_MASK (1 << 4)
+#define PTE_A_MASK (1 << 6)
+#define PTE_D_MASK (1 << 7)
+
+#define ACCESS_READ 0
+#define ACCESS_WRITE 1
+#define ACCESS_CODE 2
+
+/* access = 0: read, 1 = write, 2 = code. Set the exception_pending
+ field if necessary. return 0 if OK, -1 if translation error */
+static int get_phys_addr(RISCVCPUState *s,
+ target_ulong *ppaddr, target_ulong vaddr,
+ int access)
+{
+ int mode, levels, pte_bits, pte_idx, pte_mask, pte_size_log2, xwr, priv;
+ int need_write, vaddr_shift, i, pte_addr_bits;
+ target_ulong pte_addr, pte, vaddr_mask, paddr;
+
+ if ((s->mstatus & MSTATUS_MPRV) && access != ACCESS_CODE) {
+ /* use previous priviledge */
+ priv = (s->mstatus >> MSTATUS_MPP_SHIFT) & 3;
+ } else {
+ priv = s->priv;
+ }
+
+ if (priv == PRV_M) {
+ if (s->cur_xlen < MAX_XLEN) {
+ /* truncate virtual address */
+ *ppaddr = vaddr & (((target_ulong)1 << s->cur_xlen) - 1);
+ } else {
+ *ppaddr = vaddr;
+ }
+ return 0;
+ }
+#if MAX_XLEN == 32
+ /* 32 bits */
+ mode = s->satp >> 31;
+ if (mode == 0) {
+ /* bare: no translation */
+ *ppaddr = vaddr;
+ return 0;
+ } else {
+ /* sv32 */
+ levels = 2;
+ pte_size_log2 = 2;
+ pte_addr_bits = 22;
+ }
+#else
+ mode = (s->satp >> 60) & 0xf;
+ if (mode == 0) {
+ /* bare: no translation */
+ *ppaddr = vaddr;
+ return 0;
+ } else {
+ /* sv39/sv48 */
+ levels = mode - 8 + 3;
+ pte_size_log2 = 3;
+ vaddr_shift = MAX_XLEN - (PG_SHIFT + levels * 9);
+ if ((((target_long)vaddr << vaddr_shift) >> vaddr_shift) != vaddr)
+ return -1;
+ pte_addr_bits = 44;
+ }
+#endif
+ pte_addr = (s->satp & (((target_ulong)1 << pte_addr_bits) - 1)) << PG_SHIFT;
+ pte_bits = 12 - pte_size_log2;
+ pte_mask = (1 << pte_bits) - 1;
+ for(i = 0; i < levels; i++) {
+ vaddr_shift = PG_SHIFT + pte_bits * (levels - 1 - i);
+ pte_idx = (vaddr >> vaddr_shift) & pte_mask;
+ pte_addr += pte_idx << pte_size_log2;
+ if (pte_size_log2 == 2)
+ pte = phys_read_u32(s, pte_addr);
+ else
+ pte = phys_read_u64(s, pte_addr);
+ //printf("pte=0x%08" PRIx64 "\n", pte);
+ if (!(pte & PTE_V_MASK))
+ return -1; /* invalid PTE */
+ paddr = (pte >> 10) << PG_SHIFT;
+ xwr = (pte >> 1) & 7;
+ if (xwr != 0) {
+ if (xwr == 2 || xwr == 6)
+ return -1;
+ /* priviledge check */
+ if (priv == PRV_S) {
+ if ((pte & PTE_U_MASK) && !(s->mstatus & MSTATUS_SUM))
+ return -1;
+ } else {
+ if (!(pte & PTE_U_MASK))
+ return -1;
+ }
+ /* protection check */
+ /* MXR allows read access to execute-only pages */
+ if (s->mstatus & MSTATUS_MXR)
+ xwr |= (xwr >> 2);
+
+ if (((xwr >> access) & 1) == 0)
+ return -1;
+ need_write = !(pte & PTE_A_MASK) ||
+ (!(pte & PTE_D_MASK) && access == ACCESS_WRITE);
+ pte |= PTE_A_MASK;
+ if (access == ACCESS_WRITE)
+ pte |= PTE_D_MASK;
+ if (need_write) {
+ if (pte_size_log2 == 2)
+ phys_write_u32(s, pte_addr, pte);
+ else
+ phys_write_u64(s, pte_addr, pte);
+ }
+ vaddr_mask = ((target_ulong)1 << vaddr_shift) - 1;
+ *ppaddr = (vaddr & vaddr_mask) | (paddr & ~vaddr_mask);
+ return 0;
+ } else {
+ pte_addr = paddr;
+ }
+ }
+ return -1;
+}
+
+/* return 0 if OK, != 0 if exception */
+int target_read_slow(RISCVCPUState *s, mem_uint_t *pval,
+ target_ulong addr, int size_log2)
+{
+ int size, tlb_idx, err, al;
+ target_ulong paddr, offset;
+ uint8_t *ptr;
+ PhysMemoryRange *pr;
+ mem_uint_t ret;
+
+ /* first handle unaligned accesses */
+ size = 1 << size_log2;
+ al = addr & (size - 1);
+ if (al != 0) {
+ switch(size_log2) {
+ case 1:
+ {
+ uint8_t v0, v1;
+ err = target_read_u8(s, &v0, addr);
+ if (err)
+ return err;
+ err = target_read_u8(s, &v1, addr + 1);
+ if (err)
+ return err;
+ ret = v0 | (v1 << 8);
+ }
+ break;
+ case 2:
+ {
+ uint32_t v0, v1;
+ addr -= al;
+ err = target_read_u32(s, &v0, addr);
+ if (err)
+ return err;
+ err = target_read_u32(s, &v1, addr + 4);
+ if (err)
+ return err;
+ ret = (v0 >> (al * 8)) | (v1 << (32 - al * 8));
+ }
+ break;
+#if MLEN >= 64
+ case 3:
+ {
+ uint64_t v0, v1;
+ addr -= al;
+ err = target_read_u64(s, &v0, addr);
+ if (err)
+ return err;
+ err = target_read_u64(s, &v1, addr + 8);
+ if (err)
+ return err;
+ ret = (v0 >> (al * 8)) | (v1 << (64 - al * 8));
+ }
+ break;
+#endif
+#if MLEN >= 128
+ case 4:
+ {
+ uint128_t v0, v1;
+ addr -= al;
+ err = target_read_u128(s, &v0, addr);
+ if (err)
+ return err;
+ err = target_read_u128(s, &v1, addr + 16);
+ if (err)
+ return err;
+ ret = (v0 >> (al * 8)) | (v1 << (128 - al * 8));
+ }
+ break;
+#endif
+ default:
+ abort();
+ }
+ } else {
+ if (get_phys_addr(s, &paddr, addr, ACCESS_READ)) {
+ s->pending_tval = addr;
+ s->pending_exception = CAUSE_LOAD_PAGE_FAULT;
+ return -1;
+ }
+ pr = get_phys_mem_range(s->mem_map, paddr);
+ if (!pr) {
+#ifdef DUMP_INVALID_MEM_ACCESS
+ printf("target_read_slow: invalid physical address 0x");
+ print_target_ulong(paddr);
+ printf("\n");
+#endif
+ return 0;
+ } else if (pr->is_ram) {
+ tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);
+ ptr = pr->phys_mem + (uintptr_t)(paddr - pr->addr);
+ s->tlb_read[tlb_idx].vaddr = addr & ~PG_MASK;
+ s->tlb_read[tlb_idx].mem_addend = (uintptr_t)ptr - addr;
+ switch(size_log2) {
+ case 0:
+ ret = *(uint8_t *)ptr;
+ break;
+ case 1:
+ ret = *(uint16_t *)ptr;
+ break;
+ case 2:
+ ret = *(uint32_t *)ptr;
+ break;
+#if MLEN >= 64
+ case 3:
+ ret = *(uint64_t *)ptr;
+ break;
+#endif
+#if MLEN >= 128
+ case 4:
+ ret = *(uint128_t *)ptr;
+ break;
+#endif
+ default:
+ abort();
+ }
+ } else {
+ offset = paddr - pr->addr;
+ if (((pr->devio_flags >> size_log2) & 1) != 0) {
+ ret = pr->read_func(pr->opaque, offset, size_log2);
+ }
+#if MLEN >= 64
+ else if ((pr->devio_flags & DEVIO_SIZE32) && size_log2 == 3) {
+ /* emulate 64 bit access */
+ ret = pr->read_func(pr->opaque, offset, 2);
+ ret |= (uint64_t)pr->read_func(pr->opaque, offset + 4, 2) << 32;
+
+ }
+#endif
+ else {
+#ifdef DUMP_INVALID_MEM_ACCESS
+ printf("unsupported device read access: addr=0x");
+ print_target_ulong(paddr);
+ printf(" width=%d bits\n", 1 << (3 + size_log2));
+#endif
+ ret = 0;
+ }
+ }
+ }
+ *pval = ret;
+ return 0;
+}
+
+/* return 0 if OK, != 0 if exception */
+int target_write_slow(RISCVCPUState *s, target_ulong addr,
+ mem_uint_t val, int size_log2)
+{
+ int size, i, tlb_idx, err;
+ target_ulong paddr, offset;
+ uint8_t *ptr;
+ PhysMemoryRange *pr;
+
+ /* first handle unaligned accesses */
+ size = 1 << size_log2;
+ if ((addr & (size - 1)) != 0) {
+ /* XXX: should avoid modifying the memory in case of exception */
+ for(i = 0; i < size; i++) {
+ err = target_write_u8(s, addr + i, (val >> (8 * i)) & 0xff);
+ if (err)
+ return err;
+ }
+ } else {
+ if (get_phys_addr(s, &paddr, addr, ACCESS_WRITE)) {
+ s->pending_tval = addr;
+ s->pending_exception = CAUSE_STORE_PAGE_FAULT;
+ return -1;
+ }
+ pr = get_phys_mem_range(s->mem_map, paddr);
+ if (!pr) {
+#ifdef DUMP_INVALID_MEM_ACCESS
+ printf("target_write_slow: invalid physical address 0x");
+ print_target_ulong(paddr);
+ printf("\n");
+#endif
+ } else if (pr->is_ram) {
+ phys_mem_set_dirty_bit(pr, paddr - pr->addr);
+ tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);
+ ptr = pr->phys_mem + (uintptr_t)(paddr - pr->addr);
+ s->tlb_write[tlb_idx].vaddr = addr & ~PG_MASK;
+ s->tlb_write[tlb_idx].mem_addend = (uintptr_t)ptr - addr;
+ switch(size_log2) {
+ case 0:
+ *(uint8_t *)ptr = val;
+ break;
+ case 1:
+ *(uint16_t *)ptr = val;
+ break;
+ case 2:
+ *(uint32_t *)ptr = val;
+ break;
+#if MLEN >= 64
+ case 3:
+ *(uint64_t *)ptr = val;
+ break;
+#endif
+#if MLEN >= 128
+ case 4:
+ *(uint128_t *)ptr = val;
+ break;
+#endif
+ default:
+ abort();
+ }
+ } else {
+ offset = paddr - pr->addr;
+ if (((pr->devio_flags >> size_log2) & 1) != 0) {
+ pr->write_func(pr->opaque, offset, val, size_log2);
+ }
+#if MLEN >= 64
+ else if ((pr->devio_flags & DEVIO_SIZE32) && size_log2 == 3) {
+ /* emulate 64 bit access */
+ pr->write_func(pr->opaque, offset,
+ val & 0xffffffff, 2);
+ pr->write_func(pr->opaque, offset + 4,
+ (val >> 32) & 0xffffffff, 2);
+ }
+#endif
+ else {
+#ifdef DUMP_INVALID_MEM_ACCESS
+ printf("unsupported device write access: addr=0x");
+ print_target_ulong(paddr);
+ printf(" width=%d bits\n", 1 << (3 + size_log2));
+#endif
+ }
+ }
+ }
+ return 0;
+}
+
+struct __attribute__((packed)) unaligned_u32 {
+ uint32_t u32;
+};
+
+/* unaligned access at an address known to be a multiple of 2 */
+static uint32_t get_insn32(uint8_t *ptr)
+{
+#if defined(EMSCRIPTEN)
+ return ((uint16_t *)ptr)[0] | (((uint16_t *)ptr)[1] << 16);
+#else
+ return ((struct unaligned_u32 *)ptr)->u32;
+#endif
+}
+
+/* return 0 if OK, != 0 if exception */
+static no_inline __exception int target_read_insn_slow(RISCVCPUState *s,
+ uint8_t **pptr,
+ target_ulong addr)
+{
+ int tlb_idx;
+ target_ulong paddr;
+ uint8_t *ptr;
+ PhysMemoryRange *pr;
+
+ if (get_phys_addr(s, &paddr, addr, ACCESS_CODE)) {
+ s->pending_tval = addr;
+ s->pending_exception = CAUSE_FETCH_PAGE_FAULT;
+ return -1;
+ }
+ pr = get_phys_mem_range(s->mem_map, paddr);
+ if (!pr || !pr->is_ram) {
+ /* XXX: we only access to execute code from RAM */
+ s->pending_tval = addr;
+ s->pending_exception = CAUSE_FAULT_FETCH;
+ return -1;
+ }
+ tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);
+ ptr = pr->phys_mem + (uintptr_t)(paddr - pr->addr);
+ s->tlb_code[tlb_idx].vaddr = addr & ~PG_MASK;
+ s->tlb_code[tlb_idx].mem_addend = (uintptr_t)ptr - addr;
+ *pptr = ptr;
+ return 0;
+}
+
+/* addr must be aligned */
+static inline __exception int target_read_insn_u16(RISCVCPUState *s, uint16_t *pinsn,
+ target_ulong addr)
+{
+ uint32_t tlb_idx;
+ uint8_t *ptr;
+
+ tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);
+ if (likely(s->tlb_code[tlb_idx].vaddr == (addr & ~PG_MASK))) {
+ ptr = (uint8_t *)(s->tlb_code[tlb_idx].mem_addend +
+ (uintptr_t)addr);
+ } else {
+ if (target_read_insn_slow(s, &ptr, addr))
+ return -1;
+ }
+ *pinsn = *(uint16_t *)ptr;
+ return 0;
+}
+
+static void tlb_init(RISCVCPUState *s)
+{
+ int i;
+
+ for(i = 0; i < TLB_SIZE; i++) {
+ s->tlb_read[i].vaddr = -1;
+ s->tlb_write[i].vaddr = -1;
+ s->tlb_code[i].vaddr = -1;
+ }
+}
+
+static void tlb_flush_all(RISCVCPUState *s)
+{
+ tlb_init(s);
+}
+
+static void tlb_flush_vaddr(RISCVCPUState *s, target_ulong vaddr)
+{
+ tlb_flush_all(s);
+}
+
+/* XXX: inefficient but not critical as long as it is seldom used */
+static void glue(riscv_cpu_flush_tlb_write_range_ram,
+ MAX_XLEN)(RISCVCPUState *s,
+ uint8_t *ram_ptr, size_t ram_size)
+{
+ uint8_t *ptr, *ram_end;
+ int i;
+
+ ram_end = ram_ptr + ram_size;
+ for(i = 0; i < TLB_SIZE; i++) {
+ if (s->tlb_write[i].vaddr != -1) {
+ ptr = (uint8_t *)(s->tlb_write[i].mem_addend +
+ (uintptr_t)s->tlb_write[i].vaddr);
+ if (ptr >= ram_ptr && ptr < ram_end) {
+ s->tlb_write[i].vaddr = -1;
+ }
+ }
+ }
+}
+
+
+#define SSTATUS_MASK0 (MSTATUS_UIE | MSTATUS_SIE | \
+ MSTATUS_UPIE | MSTATUS_SPIE | \
+ MSTATUS_SPP | \
+ MSTATUS_FS | MSTATUS_XS | \
+ MSTATUS_SUM | MSTATUS_MXR)
+#if MAX_XLEN >= 64
+#define SSTATUS_MASK (SSTATUS_MASK0 | MSTATUS_UXL_MASK)
+#else
+#define SSTATUS_MASK SSTATUS_MASK0
+#endif
+
+
+#define MSTATUS_MASK (MSTATUS_UIE | MSTATUS_SIE | MSTATUS_MIE | \
+ MSTATUS_UPIE | MSTATUS_SPIE | MSTATUS_MPIE | \
+ MSTATUS_SPP | MSTATUS_MPP | \
+ MSTATUS_FS | \
+ MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_MXR)
+
+/* cycle and insn counters */
+#define COUNTEREN_MASK ((1 << 0) | (1 << 2))
+
+/* return the complete mstatus with the SD bit */
+static target_ulong get_mstatus(RISCVCPUState *s, target_ulong mask)
+{
+ target_ulong val;
+ BOOL sd;
+ val = s->mstatus | (s->fs << MSTATUS_FS_SHIFT);
+ val &= mask;
+ sd = ((val & MSTATUS_FS) == MSTATUS_FS) |
+ ((val & MSTATUS_XS) == MSTATUS_XS);
+ if (sd)
+ val |= (target_ulong)1 << (s->cur_xlen - 1);
+ return val;
+}
+
+static int get_base_from_xlen(int xlen)
+{
+ if (xlen == 32)
+ return 1;
+ else if (xlen == 64)
+ return 2;
+ else
+ return 3;
+}
+
+static void set_mstatus(RISCVCPUState *s, target_ulong val)
+{
+ target_ulong mod, mask;
+
+ /* flush the TLBs if change of MMU config */
+ mod = s->mstatus ^ val;
+ if ((mod & (MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_MXR)) != 0 ||
+ ((s->mstatus & MSTATUS_MPRV) && (mod & MSTATUS_MPP) != 0)) {
+ tlb_flush_all(s);
+ }
+ s->fs = (val >> MSTATUS_FS_SHIFT) & 3;
+
+ mask = MSTATUS_MASK & ~MSTATUS_FS;
+#if MAX_XLEN >= 64
+ {
+ int uxl, sxl;
+ uxl = (val >> MSTATUS_UXL_SHIFT) & 3;
+ if (uxl >= 1 && uxl <= get_base_from_xlen(MAX_XLEN))
+ mask |= MSTATUS_UXL_MASK;
+ sxl = (val >> MSTATUS_UXL_SHIFT) & 3;
+ if (sxl >= 1 && sxl <= get_base_from_xlen(MAX_XLEN))
+ mask |= MSTATUS_SXL_MASK;
+ }
+#endif
+ s->mstatus = (s->mstatus & ~mask) | (val & mask);
+}
+
+/* return -1 if invalid CSR. 0 if OK. 'will_write' indicate that the
+ csr will be written after (used for CSR access check) */
+static int csr_read(RISCVCPUState *s, target_ulong *pval, uint32_t csr,
+ BOOL will_write)
+{
+ target_ulong val;
+
+ if (((csr & 0xc00) == 0xc00) && will_write)
+ return -1; /* read-only CSR */
+ if (s->priv < ((csr >> 8) & 3))
+ return -1; /* not enough priviledge */
+
+ switch(csr) {
+#if FLEN > 0
+ case 0x001: /* fflags */
+ if (s->fs == 0)
+ return -1;
+ val = s->fflags;
+ break;
+ case 0x002: /* frm */
+ if (s->fs == 0)
+ return -1;
+ val = s->frm;
+ break;
+ case 0x003:
+ if (s->fs == 0)
+ return -1;
+ val = s->fflags | (s->frm << 5);
+ break;
+#endif
+ case 0xc00: /* ucycle */
+ case 0xc02: /* uinstret */
+ {
+ uint32_t counteren;
+ if (s->priv < PRV_M) {
+ if (s->priv < PRV_S)
+ counteren = s->scounteren;
+ else
+ counteren = s->mcounteren;
+ if (((counteren >> (csr & 0x1f)) & 1) == 0)
+ goto invalid_csr;
+ }
+ }
+ val = (int64_t)s->insn_counter;
+ break;
+ case 0xc80: /* mcycleh */
+ case 0xc82: /* minstreth */
+ if (s->cur_xlen != 32)
+ goto invalid_csr;
+ {
+ uint32_t counteren;
+ if (s->priv < PRV_M) {
+ if (s->priv < PRV_S)
+ counteren = s->scounteren;
+ else
+ counteren = s->mcounteren;
+ if (((counteren >> (csr & 0x1f)) & 1) == 0)
+ goto invalid_csr;
+ }
+ }
+ val = s->insn_counter >> 32;
+ break;
+
+ case 0x100:
+ val = get_mstatus(s, SSTATUS_MASK);
+ break;
+ case 0x104: /* sie */
+ val = s->mie & s->mideleg;
+ break;
+ case 0x105:
+ val = s->stvec;
+ break;
+ case 0x106:
+ val = s->scounteren;
+ break;
+ case 0x140:
+ val = s->sscratch;
+ break;
+ case 0x141:
+ val = s->sepc;
+ break;
+ case 0x142:
+ val = s->scause;
+ break;
+ case 0x143:
+ val = s->stval;
+ break;
+ case 0x144: /* sip */
+ val = s->mip & s->mideleg;
+ break;
+ case 0x180:
+ val = s->satp;
+ break;
+ case 0x300:
+ val = get_mstatus(s, (target_ulong)-1);
+ break;
+ case 0x301:
+ val = s->misa;
+ val |= (target_ulong)s->mxl << (s->cur_xlen - 2);
+ break;
+ case 0x302:
+ val = s->medeleg;
+ break;
+ case 0x303:
+ val = s->mideleg;
+ break;
+ case 0x304:
+ val = s->mie;
+ break;
+ case 0x305:
+ val = s->mtvec;
+ break;
+ case 0x306:
+ val = s->mcounteren;
+ break;
+ case 0x340:
+ val = s->mscratch;
+ break;
+ case 0x341:
+ val = s->mepc;
+ break;
+ case 0x342:
+ val = s->mcause;
+ break;
+ case 0x343:
+ val = s->mtval;
+ break;
+ case 0x344:
+ val = s->mip;
+ break;
+ case 0xb00: /* mcycle */
+ case 0xb02: /* minstret */
+ val = (int64_t)s->insn_counter;
+ break;
+ case 0xb80: /* mcycleh */
+ case 0xb82: /* minstreth */
+ if (s->cur_xlen != 32)
+ goto invalid_csr;
+ val = s->insn_counter >> 32;
+ break;
+ case 0xf14:
+ val = s->mhartid;
+ break;
+ default:
+ invalid_csr:
+#ifdef DUMP_INVALID_CSR
+ /* the 'time' counter is usually emulated */
+ if (csr != 0xc01 && csr != 0xc81) {
+ printf("csr_read: invalid CSR=0x%x\n", csr);
+ }
+#endif
+ *pval = 0;
+ return -1;
+ }
+ *pval = val;
+ return 0;
+}
+
+#if FLEN > 0
+static void set_frm(RISCVCPUState *s, unsigned int val)
+{
+ if (val >= 5)
+ val = 0;
+ s->frm = val;
+}
+
+/* return -1 if invalid roundind mode */
+static int get_insn_rm(RISCVCPUState *s, unsigned int rm)
+{
+ if (rm == 7)
+ return s->frm;
+ if (rm >= 5)
+ return -1;
+ else
+ return rm;
+}
+#endif
+
+/* return -1 if invalid CSR, 0 if OK, 1 if the interpreter loop must be
+ exited (e.g. XLEN was modified), 2 if TLBs have been flushed. */
+static int csr_write(RISCVCPUState *s, uint32_t csr, target_ulong val)
+{
+ target_ulong mask;
+
+#if defined(DUMP_CSR)
+ printf("csr_write: csr=0x%03x val=0x", csr);
+ print_target_ulong(val);
+ printf("\n");
+#endif
+ switch(csr) {
+#if FLEN > 0
+ case 0x001: /* fflags */
+ s->fflags = val & 0x1f;
+ s->fs = 3;
+ break;
+ case 0x002: /* frm */
+ set_frm(s, val & 7);
+ s->fs = 3;
+ break;
+ case 0x003: /* fcsr */
+ set_frm(s, (val >> 5) & 7);
+ s->fflags = val & 0x1f;
+ s->fs = 3;
+ break;
+#endif
+ case 0x100: /* sstatus */
+ set_mstatus(s, (s->mstatus & ~SSTATUS_MASK) | (val & SSTATUS_MASK));
+ break;
+ case 0x104: /* sie */
+ mask = s->mideleg;
+ s->mie = (s->mie & ~mask) | (val & mask);
+ break;
+ case 0x105:
+ s->stvec = val & ~3;
+ break;
+ case 0x106:
+ s->scounteren = val & COUNTEREN_MASK;
+ break;
+ case 0x140:
+ s->sscratch = val;
+ break;
+ case 0x141:
+ s->sepc = val & ~1;
+ break;
+ case 0x142:
+ s->scause = val;
+ break;
+ case 0x143:
+ s->stval = val;
+ break;
+ case 0x144: /* sip */
+ mask = s->mideleg;
+ s->mip = (s->mip & ~mask) | (val & mask);
+ break;
+ case 0x180:
+ /* no ASID implemented */
+#if MAX_XLEN == 32
+ {
+ int new_mode;
+ new_mode = (val >> 31) & 1;
+ s->satp = (val & (((target_ulong)1 << 22) - 1)) |
+ (new_mode << 31);
+ }
+#else
+ {
+ int mode, new_mode;
+ mode = s->satp >> 60;
+ new_mode = (val >> 60) & 0xf;
+ if (new_mode == 0 || (new_mode >= 8 && new_mode <= 9))
+ mode = new_mode;
+ s->satp = (val & (((uint64_t)1 << 44) - 1)) |
+ ((uint64_t)mode << 60);
+ }
+#endif
+ tlb_flush_all(s);
+ return 2;
+
+ case 0x300:
+ set_mstatus(s, val);
+ break;
+ case 0x301: /* misa */
+#if MAX_XLEN >= 64
+ {
+ int new_mxl;
+ new_mxl = (val >> (s->cur_xlen - 2)) & 3;
+ if (new_mxl >= 1 && new_mxl <= get_base_from_xlen(MAX_XLEN)) {
+ /* Note: misa is only modified in M level, so cur_xlen
+ = 2^(mxl + 4) */
+ if (s->mxl != new_mxl) {
+ s->mxl = new_mxl;
+ s->cur_xlen = 1 << (new_mxl + 4);
+ return 1;
+ }
+ }
+ }
+#endif
+ break;
+ case 0x302:
+ mask = (1 << (CAUSE_STORE_PAGE_FAULT + 1)) - 1;
+ s->medeleg = (s->medeleg & ~mask) | (val & mask);
+ break;
+ case 0x303:
+ mask = MIP_SSIP | MIP_STIP | MIP_SEIP;
+ s->mideleg = (s->mideleg & ~mask) | (val & mask);
+ break;
+ case 0x304:
+ mask = MIP_MSIP | MIP_MTIP | MIP_SSIP | MIP_STIP | MIP_SEIP;
+ s->mie = (s->mie & ~mask) | (val & mask);
+ break;
+ case 0x305:
+ s->mtvec = val & ~3;
+ break;
+ case 0x306:
+ s->mcounteren = val & COUNTEREN_MASK;
+ break;
+ case 0x340:
+ s->mscratch = val;
+ break;
+ case 0x341:
+ s->mepc = val & ~1;
+ break;
+ case 0x342:
+ s->mcause = val;
+ break;
+ case 0x343:
+ s->mtval = val;
+ break;
+ case 0x344:
+ mask = MIP_SSIP | MIP_STIP;
+ s->mip = (s->mip & ~mask) | (val & mask);
+ break;
+ default:
+#ifdef DUMP_INVALID_CSR
+ printf("csr_write: invalid CSR=0x%x\n", csr);
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+static void set_priv(RISCVCPUState *s, int priv)
+{
+ if (s->priv != priv) {
+ tlb_flush_all(s);
+#if MAX_XLEN >= 64
+ /* change the current xlen */
+ {
+ int mxl;
+ if (priv == PRV_S)
+ mxl = (s->mstatus >> MSTATUS_SXL_SHIFT) & 3;
+ else if (priv == PRV_U)
+ mxl = (s->mstatus >> MSTATUS_UXL_SHIFT) & 3;
+ else
+ mxl = s->mxl;
+ s->cur_xlen = 1 << (4 + mxl);
+ }
+#endif
+ s->priv = priv;
+ }
+}
+
+static void raise_exception2(RISCVCPUState *s, uint32_t cause,
+ target_ulong tval)
+{
+ BOOL deleg;
+ target_ulong causel;
+
+#if defined(DUMP_EXCEPTIONS) || defined(DUMP_MMU_EXCEPTIONS) || defined(DUMP_INTERRUPTS)
+ {
+ int flag;
+ flag = 0;
+#ifdef DUMP_MMU_EXCEPTIONS
+ if (cause == CAUSE_FAULT_FETCH ||
+ cause == CAUSE_FAULT_LOAD ||
+ cause == CAUSE_FAULT_STORE ||
+ cause == CAUSE_FETCH_PAGE_FAULT ||
+ cause == CAUSE_LOAD_PAGE_FAULT ||
+ cause == CAUSE_STORE_PAGE_FAULT)
+ flag = 1;
+#endif
+#ifdef DUMP_INTERRUPTS
+ flag |= (cause & CAUSE_INTERRUPT) != 0;
+#endif
+#ifdef DUMP_EXCEPTIONS
+ flag = 1;
+ flag = (cause & CAUSE_INTERRUPT) == 0;
+ if (cause == CAUSE_SUPERVISOR_ECALL || cause == CAUSE_ILLEGAL_INSTRUCTION)
+ flag = 0;
+#endif
+ if (flag) {
+ log_printf("raise_exception: cause=0x%08x tval=0x", cause);
+#ifdef CONFIG_LOGFILE
+ fprint_target_ulong(log_file, tval);
+#else
+ print_target_ulong(tval);
+#endif
+ log_printf("\n");
+ dump_regs(s);
+ }
+ }
+#endif
+
+ if (s->priv <= PRV_S) {
+ /* delegate the exception to the supervisor priviledge */
+ if (cause & CAUSE_INTERRUPT)
+ deleg = (s->mideleg >> (cause & (MAX_XLEN - 1))) & 1;
+ else
+ deleg = (s->medeleg >> cause) & 1;
+ } else {
+ deleg = 0;
+ }
+
+ causel = cause & 0x7fffffff;
+ if (cause & CAUSE_INTERRUPT)
+ causel |= (target_ulong)1 << (s->cur_xlen - 1);
+
+ if (deleg) {
+ s->scause = causel;
+ s->sepc = s->pc;
+ s->stval = tval;
+ s->mstatus = (s->mstatus & ~MSTATUS_SPIE) |
+ (((s->mstatus >> s->priv) & 1) << MSTATUS_SPIE_SHIFT);
+ s->mstatus = (s->mstatus & ~MSTATUS_SPP) |
+ (s->priv << MSTATUS_SPP_SHIFT);
+ s->mstatus &= ~MSTATUS_SIE;
+ set_priv(s, PRV_S);
+ s->pc = s->stvec;
+ } else {
+ s->mcause = causel;
+ s->mepc = s->pc;
+ s->mtval = tval;
+ s->mstatus = (s->mstatus & ~MSTATUS_MPIE) |
+ (((s->mstatus >> s->priv) & 1) << MSTATUS_MPIE_SHIFT);
+ s->mstatus = (s->mstatus & ~MSTATUS_MPP) |
+ (s->priv << MSTATUS_MPP_SHIFT);
+ s->mstatus &= ~MSTATUS_MIE;
+ set_priv(s, PRV_M);
+ s->pc = s->mtvec;
+ }
+}
+
+static void raise_exception(RISCVCPUState *s, uint32_t cause)
+{
+ raise_exception2(s, cause, 0);
+}
+
+static void handle_sret(RISCVCPUState *s)
+{
+ int spp, spie;
+ spp = (s->mstatus >> MSTATUS_SPP_SHIFT) & 1;
+ /* set the IE state to previous IE state */
+ spie = (s->mstatus >> MSTATUS_SPIE_SHIFT) & 1;
+ s->mstatus = (s->mstatus & ~(1 << spp)) |
+ (spie << spp);
+ /* set SPIE to 1 */
+ s->mstatus |= MSTATUS_SPIE;
+ /* set SPP to U */
+ s->mstatus &= ~MSTATUS_SPP;
+ set_priv(s, spp);
+ s->pc = s->sepc;
+}
+
+static void handle_mret(RISCVCPUState *s)
+{
+ int mpp, mpie;
+ mpp = (s->mstatus >> MSTATUS_MPP_SHIFT) & 3;
+ /* set the IE state to previous IE state */
+ mpie = (s->mstatus >> MSTATUS_MPIE_SHIFT) & 1;
+ s->mstatus = (s->mstatus & ~(1 << mpp)) |
+ (mpie << mpp);
+ /* set MPIE to 1 */
+ s->mstatus |= MSTATUS_MPIE;
+ /* set MPP to U */
+ s->mstatus &= ~MSTATUS_MPP;
+ set_priv(s, mpp);
+ s->pc = s->mepc;
+}
+
+static inline uint32_t get_pending_irq_mask(RISCVCPUState *s)
+{
+ uint32_t pending_ints, enabled_ints;
+
+ pending_ints = s->mip & s->mie;
+ if (pending_ints == 0)
+ return 0;
+
+ enabled_ints = 0;
+ switch(s->priv) {
+ case PRV_M:
+ if (s->mstatus & MSTATUS_MIE)
+ enabled_ints = ~s->mideleg;
+ break;
+ case PRV_S:
+ enabled_ints = ~s->mideleg;
+ if (s->mstatus & MSTATUS_SIE)
+ enabled_ints |= s->mideleg;
+ break;
+ default:
+ case PRV_U:
+ enabled_ints = -1;
+ break;
+ }
+ return pending_ints & enabled_ints;
+}
+
+static __exception int raise_interrupt(RISCVCPUState *s)
+{
+ uint32_t mask;
+ int irq_num;
+
+ mask = get_pending_irq_mask(s);
+ if (mask == 0)
+ return 0;
+ irq_num = ctz32(mask);
+ raise_exception(s, irq_num | CAUSE_INTERRUPT);
+ return -1;
+}
+
+static inline int32_t sext(int32_t val, int n)
+{
+ return (val << (32 - n)) >> (32 - n);
+}
+
+static inline uint32_t get_field1(uint32_t val, int src_pos,
+ int dst_pos, int dst_pos_max)
+{
+ int mask;
+ assert(dst_pos_max >= dst_pos);
+ mask = ((1 << (dst_pos_max - dst_pos + 1)) - 1) << dst_pos;
+ if (dst_pos >= src_pos)
+ return (val << (dst_pos - src_pos)) & mask;
+ else
+ return (val >> (src_pos - dst_pos)) & mask;
+}
+
+#define XLEN 32
+#include "riscv_cpu_template.h"
+
+#if MAX_XLEN >= 64
+#define XLEN 64
+#include "riscv_cpu_template.h"
+#endif
+
+#if MAX_XLEN >= 128
+#define XLEN 128
+#include "riscv_cpu_template.h"
+#endif
+
+static void glue(riscv_cpu_interp, MAX_XLEN)(RISCVCPUState *s, int n_cycles)
+{
+#ifdef USE_GLOBAL_STATE
+ s = &riscv_cpu_global_state;
+#endif
+ uint64_t timeout;
+
+ timeout = s->insn_counter + n_cycles;
+ while (!s->power_down_flag &&
+ (int)(timeout - s->insn_counter) > 0) {
+ n_cycles = timeout - s->insn_counter;
+ switch(s->cur_xlen) {
+ case 32:
+ riscv_cpu_interp_x32(s, n_cycles);
+ break;
+#if MAX_XLEN >= 64
+ case 64:
+ riscv_cpu_interp_x64(s, n_cycles);
+ break;
+#endif
+#if MAX_XLEN >= 128
+ case 128:
+ riscv_cpu_interp_x128(s, n_cycles);
+ break;
+#endif
+ default:
+ abort();
+ }
+ }
+}
+
+/* Note: the value is not accurate when called in riscv_cpu_interp() */
+static uint64_t glue(riscv_cpu_get_cycles, MAX_XLEN)(RISCVCPUState *s)
+{
+ return s->insn_counter;
+}
+
+static void glue(riscv_cpu_set_mip, MAX_XLEN)(RISCVCPUState *s, uint32_t mask)
+{
+ s->mip |= mask;
+ /* exit from power down if an interrupt is pending */
+ if (s->power_down_flag && (s->mip & s->mie) != 0)
+ s->power_down_flag = FALSE;
+}
+
+static void glue(riscv_cpu_reset_mip, MAX_XLEN)(RISCVCPUState *s, uint32_t mask)
+{
+ s->mip &= ~mask;
+}
+
+static uint32_t glue(riscv_cpu_get_mip, MAX_XLEN)(RISCVCPUState *s)
+{
+ return s->mip;
+}
+
+static BOOL glue(riscv_cpu_get_power_down, MAX_XLEN)(RISCVCPUState *s)
+{
+ return s->power_down_flag;
+}
+
+static RISCVCPUState *glue(riscv_cpu_init, MAX_XLEN)(PhysMemoryMap *mem_map)
+{
+ RISCVCPUState *s;
+
+#ifdef USE_GLOBAL_STATE
+ s = &riscv_cpu_global_state;
+#else
+ s = mallocz(sizeof(*s));
+#endif
+ s->common.class_ptr = &glue(riscv_cpu_class, MAX_XLEN);
+ s->mem_map = mem_map;
+ s->pc = 0x1000;
+ s->priv = PRV_M;
+ s->cur_xlen = MAX_XLEN;
+ s->mxl = get_base_from_xlen(MAX_XLEN);
+ s->mstatus = ((uint64_t)s->mxl << MSTATUS_UXL_SHIFT) |
+ ((uint64_t)s->mxl << MSTATUS_SXL_SHIFT);
+ s->misa |= MCPUID_SUPER | MCPUID_USER | MCPUID_I | MCPUID_M | MCPUID_A;
+#if FLEN >= 32
+ s->misa |= MCPUID_F;
+#endif
+#if FLEN >= 64
+ s->misa |= MCPUID_D;
+#endif
+#if FLEN >= 128
+ s->misa |= MCPUID_Q;
+#endif
+#ifdef CONFIG_EXT_C
+ s->misa |= MCPUID_C;
+#endif
+ tlb_init(s);
+ return s;
+}
+
+static void glue(riscv_cpu_end, MAX_XLEN)(RISCVCPUState *s)
+{
+#ifdef USE_GLOBAL_STATE
+ free(s);
+#endif
+}
+
+static uint32_t glue(riscv_cpu_get_misa, MAX_XLEN)(RISCVCPUState *s)
+{
+ return s->misa;
+}
+
+const RISCVCPUClass glue(riscv_cpu_class, MAX_XLEN) = {
+ glue(riscv_cpu_init, MAX_XLEN),
+ glue(riscv_cpu_end, MAX_XLEN),
+ glue(riscv_cpu_interp, MAX_XLEN),
+ glue(riscv_cpu_get_cycles, MAX_XLEN),
+ glue(riscv_cpu_set_mip, MAX_XLEN),
+ glue(riscv_cpu_reset_mip, MAX_XLEN),
+ glue(riscv_cpu_get_mip, MAX_XLEN),
+ glue(riscv_cpu_get_power_down, MAX_XLEN),
+ glue(riscv_cpu_get_misa, MAX_XLEN),
+ glue(riscv_cpu_flush_tlb_write_range_ram, MAX_XLEN),
+};
+
+#if CONFIG_RISCV_MAX_XLEN == MAX_XLEN
+RISCVCPUState *riscv_cpu_init(PhysMemoryMap *mem_map, int max_xlen)
+{
+ const RISCVCPUClass *c;
+ switch(max_xlen) {
+ /* with emscripten we compile a single CPU */
+#if defined(EMSCRIPTEN)
+ case MAX_XLEN:
+ c = &glue(riscv_cpu_class, MAX_XLEN);
+ break;
+#else
+ case 32:
+ c = &riscv_cpu_class32;
+ break;
+ case 64:
+ c = &riscv_cpu_class64;
+ break;
+#if CONFIG_RISCV_MAX_XLEN == 128
+ case 128:
+ c = &riscv_cpu_class128;
+ break;
+#endif
+#endif /* !EMSCRIPTEN */
+ default:
+ return NULL;
+ }
+ return c->riscv_cpu_init(mem_map);
+}
+#endif /* CONFIG_RISCV_MAX_XLEN == MAX_XLEN */
+
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu.h b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu.h
new file mode 100644
index 0000000..6fb8b37
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu.h
@@ -0,0 +1,118 @@
+/*
+ * RISCV CPU emulator
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef RISCV_CPU_H
+#define RISCV_CPU_H
+
+#include <stdlib.h>
+#include "cutils.h"
+#include "iomem.h"
+
+#define MIP_USIP (1 << 0)
+#define MIP_SSIP (1 << 1)
+#define MIP_HSIP (1 << 2)
+#define MIP_MSIP (1 << 3)
+#define MIP_UTIP (1 << 4)
+#define MIP_STIP (1 << 5)
+#define MIP_HTIP (1 << 6)
+#define MIP_MTIP (1 << 7)
+#define MIP_UEIP (1 << 8)
+#define MIP_SEIP (1 << 9)
+#define MIP_HEIP (1 << 10)
+#define MIP_MEIP (1 << 11)
+
+typedef struct RISCVCPUState RISCVCPUState;
+
+typedef struct {
+ RISCVCPUState *(*riscv_cpu_init)(PhysMemoryMap *mem_map);
+ void (*riscv_cpu_end)(RISCVCPUState *s);
+ void (*riscv_cpu_interp)(RISCVCPUState *s, int n_cycles);
+ uint64_t (*riscv_cpu_get_cycles)(RISCVCPUState *s);
+ void (*riscv_cpu_set_mip)(RISCVCPUState *s, uint32_t mask);
+ void (*riscv_cpu_reset_mip)(RISCVCPUState *s, uint32_t mask);
+ uint32_t (*riscv_cpu_get_mip)(RISCVCPUState *s);
+ BOOL (*riscv_cpu_get_power_down)(RISCVCPUState *s);
+ uint32_t (*riscv_cpu_get_misa)(RISCVCPUState *s);
+ void (*riscv_cpu_flush_tlb_write_range_ram)(RISCVCPUState *s,
+ uint8_t *ram_ptr, size_t ram_size);
+} RISCVCPUClass;
+
+typedef struct {
+ const RISCVCPUClass *class_ptr;
+} RISCVCPUCommonState;
+
+int riscv_cpu_get_max_xlen(void);
+
+extern const RISCVCPUClass riscv_cpu_class32;
+extern const RISCVCPUClass riscv_cpu_class64;
+extern const RISCVCPUClass riscv_cpu_class128;
+
+RISCVCPUState *riscv_cpu_init(PhysMemoryMap *mem_map, int max_xlen);
+static inline void riscv_cpu_end(RISCVCPUState *s)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ c->riscv_cpu_end(s);
+}
+static inline void riscv_cpu_interp(RISCVCPUState *s, int n_cycles)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ c->riscv_cpu_interp(s, n_cycles);
+}
+static inline uint64_t riscv_cpu_get_cycles(RISCVCPUState *s)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ return c->riscv_cpu_get_cycles(s);
+}
+static inline void riscv_cpu_set_mip(RISCVCPUState *s, uint32_t mask)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ c->riscv_cpu_set_mip(s, mask);
+}
+static inline void riscv_cpu_reset_mip(RISCVCPUState *s, uint32_t mask)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ c->riscv_cpu_reset_mip(s, mask);
+}
+static inline uint32_t riscv_cpu_get_mip(RISCVCPUState *s)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ return c->riscv_cpu_get_mip(s);
+}
+static inline BOOL riscv_cpu_get_power_down(RISCVCPUState *s)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ return c->riscv_cpu_get_power_down(s);
+}
+static inline uint32_t riscv_cpu_get_misa(RISCVCPUState *s)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ return c->riscv_cpu_get_misa(s);
+}
+static inline void riscv_cpu_flush_tlb_write_range_ram(RISCVCPUState *s,
+ uint8_t *ram_ptr, size_t ram_size)
+{
+ const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
+ c->riscv_cpu_flush_tlb_write_range_ram(s, ram_ptr, ram_size);
+}
+
+#endif /* RISCV_CPU_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_fp_template.h b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_fp_template.h
new file mode 100644
index 0000000..88d4037
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_fp_template.h
@@ -0,0 +1,304 @@
+/*
+ * RISCV emulator
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#if F_SIZE == 32
+#define OPID 0
+#define F_HIGH F32_HIGH
+#elif F_SIZE == 64
+#define OPID 1
+#define F_HIGH F64_HIGH
+#elif F_SIZE == 128
+#define OPID 3
+#define F_HIGH 0
+#else
+#error unsupported F_SIZE
+#endif
+
+#define FSIGN_MASK glue(FSIGN_MASK, F_SIZE)
+
+ case (0x00 << 2) | OPID:
+ rm = get_insn_rm(s, rm);
+ if (rm < 0)
+ goto illegal_insn;
+ s->fp_reg[rd] = glue(add_sf, F_SIZE)(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ rm, &s->fflags) | F_HIGH;
+ s->fs = 3;
+ break;
+ case (0x01 << 2) | OPID:
+ rm = get_insn_rm(s, rm);
+ if (rm < 0)
+ goto illegal_insn;
+ s->fp_reg[rd] = glue(sub_sf, F_SIZE)(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ rm, &s->fflags) | F_HIGH;
+ s->fs = 3;
+ break;
+ case (0x02 << 2) | OPID:
+ rm = get_insn_rm(s, rm);
+ if (rm < 0)
+ goto illegal_insn;
+ s->fp_reg[rd] = glue(mul_sf, F_SIZE)(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ rm, &s->fflags) | F_HIGH;
+ s->fs = 3;
+ break;
+ case (0x03 << 2) | OPID:
+ rm = get_insn_rm(s, rm);
+ if (rm < 0)
+ goto illegal_insn;
+ s->fp_reg[rd] = glue(div_sf, F_SIZE)(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ rm, &s->fflags) | F_HIGH;
+ s->fs = 3;
+ break;
+ case (0x0b << 2) | OPID:
+ rm = get_insn_rm(s, rm);
+ if (rm < 0 || rs2 != 0)
+ goto illegal_insn;
+ s->fp_reg[rd] = glue(sqrt_sf, F_SIZE)(s->fp_reg[rs1],
+ rm, &s->fflags) | F_HIGH;
+ s->fs = 3;
+ break;
+ case (0x04 << 2) | OPID:
+ switch(rm) {
+ case 0: /* fsgnj */
+ s->fp_reg[rd] = (s->fp_reg[rs1] & ~FSIGN_MASK) |
+ (s->fp_reg[rs2] & FSIGN_MASK);
+ break;
+ case 1: /* fsgnjn */
+ s->fp_reg[rd] = (s->fp_reg[rs1] & ~FSIGN_MASK) |
+ ((s->fp_reg[rs2] & FSIGN_MASK) ^ FSIGN_MASK);
+ break;
+ case 2: /* fsgnjx */
+ s->fp_reg[rd] = s->fp_reg[rs1] ^
+ (s->fp_reg[rs2] & FSIGN_MASK);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ break;
+ case (0x05 << 2) | OPID:
+ switch(rm) {
+ case 0: /* fmin */
+ s->fp_reg[rd] = glue(min_sf, F_SIZE)(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ &s->fflags,
+ FMINMAX_IEEE754_201X) | F_HIGH;
+ break;
+ case 1: /* fmax */
+ s->fp_reg[rd] = glue(max_sf, F_SIZE)(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ &s->fflags,
+ FMINMAX_IEEE754_201X) | F_HIGH;
+ break;
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ break;
+ case (0x18 << 2) | OPID:
+ rm = get_insn_rm(s, rm);
+ if (rm < 0)
+ goto illegal_insn;
+ switch(rs2) {
+ case 0: /* fcvt.w.[sdq] */
+ val = (int32_t)glue(glue(cvt_sf, F_SIZE), _i32)(s->fp_reg[rs1], rm,
+ &s->fflags);
+ break;
+ case 1: /* fcvt.wu.[sdq] */
+ val = (int32_t)glue(glue(cvt_sf, F_SIZE), _u32)(s->fp_reg[rs1], rm,
+ &s->fflags);
+ break;
+#if XLEN >= 64
+ case 2: /* fcvt.l.[sdq] */
+ val = (int64_t)glue(glue(cvt_sf, F_SIZE), _i64)(s->fp_reg[rs1], rm,
+ &s->fflags);
+ break;
+ case 3: /* fcvt.lu.[sdq] */
+ val = (int64_t)glue(glue(cvt_sf, F_SIZE), _u64)(s->fp_reg[rs1], rm,
+ &s->fflags);
+ break;
+#endif
+#if XLEN >= 128
+ /* XXX: the index is not defined in the spec */
+ case 4: /* fcvt.t.[sdq] */
+ val = glue(glue(cvt_sf, F_SIZE), _i128)(s->fp_reg[rs1], rm,
+ &s->fflags);
+ break;
+ case 5: /* fcvt.tu.[sdq] */
+ val = glue(glue(cvt_sf, F_SIZE), _u128)(s->fp_reg[rs1], rm,
+ &s->fflags);
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ break;
+ case (0x14 << 2) | OPID:
+ switch(rm) {
+ case 0: /* fle */
+ val = glue(le_sf, F_SIZE)(s->fp_reg[rs1], s->fp_reg[rs2],
+ &s->fflags);
+ break;
+ case 1: /* flt */
+ val = glue(lt_sf, F_SIZE)(s->fp_reg[rs1], s->fp_reg[rs2],
+ &s->fflags);
+ break;
+ case 2: /* feq */
+ val = glue(eq_quiet_sf, F_SIZE)(s->fp_reg[rs1], s->fp_reg[rs2],
+ &s->fflags);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ break;
+ case (0x1a << 2) | OPID:
+ rm = get_insn_rm(s, rm);
+ if (rm < 0)
+ goto illegal_insn;
+ switch(rs2) {
+ case 0: /* fcvt.[sdq].w */
+ s->fp_reg[rd] = glue(cvt_i32_sf, F_SIZE)(s->reg[rs1], rm,
+ &s->fflags) | F_HIGH;
+ break;
+ case 1: /* fcvt.[sdq].wu */
+ s->fp_reg[rd] = glue(cvt_u32_sf, F_SIZE)(s->reg[rs1], rm,
+ &s->fflags) | F_HIGH;
+ break;
+#if XLEN >= 64
+ case 2: /* fcvt.[sdq].l */
+ s->fp_reg[rd] = glue(cvt_i64_sf, F_SIZE)(s->reg[rs1], rm,
+ &s->fflags) | F_HIGH;
+ break;
+ case 3: /* fcvt.[sdq].lu */
+ s->fp_reg[rd] = glue(cvt_u64_sf, F_SIZE)(s->reg[rs1], rm,
+ &s->fflags) | F_HIGH;
+ break;
+#endif
+#if XLEN >= 128
+ /* XXX: the index is not defined in the spec */
+ case 4: /* fcvt.[sdq].t */
+ s->fp_reg[rd] = glue(cvt_i128_sf, F_SIZE)(s->reg[rs1], rm,
+ &s->fflags) | F_HIGH;
+ break;
+ case 5: /* fcvt.[sdq].tu */
+ s->fp_reg[rd] = glue(cvt_u128_sf, F_SIZE)(s->reg[rs1], rm,
+ &s->fflags) | F_HIGH;
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ break;
+
+ case (0x08 << 2) | OPID:
+ rm = get_insn_rm(s, rm);
+ if (rm < 0)
+ goto illegal_insn;
+ switch(rs2) {
+#if F_SIZE == 32 && FLEN >= 64
+ case 1: /* cvt.s.d */
+ s->fp_reg[rd] = cvt_sf64_sf32(s->fp_reg[rs1], rm, &s->fflags) | F32_HIGH;
+ break;
+#if FLEN >= 128
+ case 3: /* cvt.s.q */
+ s->fp_reg[rd] = cvt_sf128_sf32(s->fp_reg[rs1], rm, &s->fflags) | F32_HIGH;
+ break;
+#endif
+#endif /* F_SIZE == 32 */
+#if F_SIZE == 64
+ case 0: /* cvt.d.s */
+ s->fp_reg[rd] = cvt_sf32_sf64(s->fp_reg[rs1], &s->fflags) | F64_HIGH;
+ break;
+#if FLEN >= 128
+ case 1: /* cvt.d.q */
+ s->fp_reg[rd] = cvt_sf128_sf64(s->fp_reg[rs1], rm, &s->fflags) | F64_HIGH;
+ break;
+#endif
+#endif /* F_SIZE == 64 */
+#if F_SIZE == 128
+ case 0: /* cvt.q.s */
+ s->fp_reg[rd] = cvt_sf32_sf128(s->fp_reg[rs1], &s->fflags);
+ break;
+ case 1: /* cvt.q.d */
+ s->fp_reg[rd] = cvt_sf64_sf128(s->fp_reg[rs1], &s->fflags);
+ break;
+#endif /* F_SIZE == 128 */
+
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ break;
+
+ case (0x1c << 2) | OPID:
+ if (rs2 != 0)
+ goto illegal_insn;
+ switch(rm) {
+#if F_SIZE <= XLEN
+ case 0: /* fmv.x.s */
+#if F_SIZE == 32
+ val = (int32_t)s->fp_reg[rs1];
+#elif F_SIZE == 64
+ val = (int64_t)s->fp_reg[rs1];
+#else
+ val = (int128_t)s->fp_reg[rs1];
+#endif
+ break;
+#endif /* F_SIZE <= XLEN */
+ case 1: /* fclass */
+ val = glue(fclass_sf, F_SIZE)(s->fp_reg[rs1]);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ break;
+
+#if F_SIZE <= XLEN
+ case (0x1e << 2) | OPID: /* fmv.s.x */
+ if (rs2 != 0 || rm != 0)
+ goto illegal_insn;
+#if F_SIZE == 32
+ s->fp_reg[rd] = (int32_t)s->reg[rs1];
+#elif F_SIZE == 64
+ s->fp_reg[rd] = (int64_t)s->reg[rs1];
+#else
+ s->fp_reg[rd] = (int128_t)s->reg[rs1];
+#endif
+ s->fs = 3;
+ break;
+#endif /* F_SIZE <= XLEN */
+
+#undef F_SIZE
+#undef F_HIGH
+#undef OPID
+#undef FSIGN_MASK
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_priv.h b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_priv.h
new file mode 100644
index 0000000..983d8b8
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_priv.h
@@ -0,0 +1,293 @@
+/*
+ * RISCV CPU emulator private definitions
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef RISCV_CPU_PRIV_H
+#define RISCV_CPU_PRIV_H
+
+#include "riscv_cpu.h"
+#include "cutils.h"
+
+#define __exception __attribute__((warn_unused_result))
+
+#ifndef FLEN
+#if MAX_XLEN == 128
+#define FLEN 128
+#else
+#define FLEN 64
+#endif
+#endif /* !FLEN */
+
+#define CONFIG_EXT_C /* compressed instructions */
+
+#if defined(EMSCRIPTEN)
+#define USE_GLOBAL_STATE
+/* use local variables slows down the generated JS code */
+#define USE_GLOBAL_VARIABLES
+#endif
+
+#if MAX_XLEN == 32
+typedef uint32_t target_ulong;
+typedef int32_t target_long;
+#define PR_target_ulong "08x"
+#elif MAX_XLEN == 64
+typedef uint64_t target_ulong;
+typedef int64_t target_long;
+#define PR_target_ulong "016" PRIx64
+#elif MAX_XLEN == 128
+typedef uint128_t target_ulong;
+typedef int128_t target_long;
+#define PR_target_ulong "016" PRIx64 /* XXX */
+#else
+#error unsupported MAX_XLEN
+#endif
+
+/* FLEN is the floating point register width */
+#if FLEN > 0
+#if FLEN == 32
+typedef uint32_t fp_uint;
+#define F32_HIGH 0
+#elif FLEN == 64
+typedef uint64_t fp_uint;
+#define F32_HIGH ((fp_uint)-1 << 32)
+#define F64_HIGH 0
+#elif FLEN == 128
+typedef uint128_t fp_uint;
+#define F32_HIGH ((fp_uint)-1 << 32)
+#define F64_HIGH ((fp_uint)-1 << 64)
+#else
+#error unsupported FLEN
+#endif
+#endif
+
+/* MLEN is the maximum memory access width */
+#if MAX_XLEN <= 32 && FLEN <= 32
+#define MLEN 32
+#elif MAX_XLEN <= 64 && FLEN <= 64
+#define MLEN 64
+#else
+#define MLEN 128
+#endif
+
+#if MLEN == 32
+typedef uint32_t mem_uint_t;
+#elif MLEN == 64
+typedef uint64_t mem_uint_t;
+#elif MLEN == 128
+typedef uint128_t mem_uint_t;
+#else
+#unsupported MLEN
+#endif
+
+#define TLB_SIZE 256
+
+#define CAUSE_MISALIGNED_FETCH 0x0
+#define CAUSE_FAULT_FETCH 0x1
+#define CAUSE_ILLEGAL_INSTRUCTION 0x2
+#define CAUSE_BREAKPOINT 0x3
+#define CAUSE_MISALIGNED_LOAD 0x4
+#define CAUSE_FAULT_LOAD 0x5
+#define CAUSE_MISALIGNED_STORE 0x6
+#define CAUSE_FAULT_STORE 0x7
+#define CAUSE_USER_ECALL 0x8
+#define CAUSE_SUPERVISOR_ECALL 0x9
+#define CAUSE_HYPERVISOR_ECALL 0xa
+#define CAUSE_MACHINE_ECALL 0xb
+#define CAUSE_FETCH_PAGE_FAULT 0xc
+#define CAUSE_LOAD_PAGE_FAULT 0xd
+#define CAUSE_STORE_PAGE_FAULT 0xf
+
+/* Note: converted to correct bit position at runtime */
+#define CAUSE_INTERRUPT ((uint32_t)1 << 31)
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+/* misa CSR */
+#define MCPUID_SUPER (1 << ('S' - 'A'))
+#define MCPUID_USER (1 << ('U' - 'A'))
+#define MCPUID_I (1 << ('I' - 'A'))
+#define MCPUID_M (1 << ('M' - 'A'))
+#define MCPUID_A (1 << ('A' - 'A'))
+#define MCPUID_F (1 << ('F' - 'A'))
+#define MCPUID_D (1 << ('D' - 'A'))
+#define MCPUID_Q (1 << ('Q' - 'A'))
+#define MCPUID_C (1 << ('C' - 'A'))
+
+/* mstatus CSR */
+
+#define MSTATUS_SPIE_SHIFT 5
+#define MSTATUS_MPIE_SHIFT 7
+#define MSTATUS_SPP_SHIFT 8
+#define MSTATUS_MPP_SHIFT 11
+#define MSTATUS_FS_SHIFT 13
+#define MSTATUS_UXL_SHIFT 32
+#define MSTATUS_SXL_SHIFT 34
+
+#define MSTATUS_UIE (1 << 0)
+#define MSTATUS_SIE (1 << 1)
+#define MSTATUS_HIE (1 << 2)
+#define MSTATUS_MIE (1 << 3)
+#define MSTATUS_UPIE (1 << 4)
+#define MSTATUS_SPIE (1 << MSTATUS_SPIE_SHIFT)
+#define MSTATUS_HPIE (1 << 6)
+#define MSTATUS_MPIE (1 << MSTATUS_MPIE_SHIFT)
+#define MSTATUS_SPP (1 << MSTATUS_SPP_SHIFT)
+#define MSTATUS_HPP (3 << 9)
+#define MSTATUS_MPP (3 << MSTATUS_MPP_SHIFT)
+#define MSTATUS_FS (3 << MSTATUS_FS_SHIFT)
+#define MSTATUS_XS (3 << 15)
+#define MSTATUS_MPRV (1 << 17)
+#define MSTATUS_SUM (1 << 18)
+#define MSTATUS_MXR (1 << 19)
+//#define MSTATUS_TVM (1 << 20)
+//#define MSTATUS_TW (1 << 21)
+//#define MSTATUS_TSR (1 << 22)
+#define MSTATUS_UXL_MASK ((uint64_t)3 << MSTATUS_UXL_SHIFT)
+#define MSTATUS_SXL_MASK ((uint64_t)3 << MSTATUS_SXL_SHIFT)
+
+#define PG_SHIFT 12
+#define PG_MASK ((1 << PG_SHIFT) - 1)
+
+typedef struct {
+ target_ulong vaddr;
+ uintptr_t mem_addend;
+} TLBEntry;
+
+struct RISCVCPUState {
+ RISCVCPUCommonState common; /* must be first */
+
+ target_ulong pc;
+ target_ulong reg[32];
+
+#ifdef USE_GLOBAL_VARIABLES
+ /* faster to use global variables with emscripten */
+ uint8_t *__code_ptr, *__code_end;
+ target_ulong __code_to_pc_addend;
+#endif
+
+#if FLEN > 0
+ fp_uint fp_reg[32];
+ uint32_t fflags;
+ uint8_t frm;
+#endif
+
+ uint8_t cur_xlen; /* current XLEN value, <= MAX_XLEN */
+ uint8_t priv; /* see PRV_x */
+ uint8_t fs; /* MSTATUS_FS value */
+ uint8_t mxl; /* MXL field in MISA register */
+
+ int32_t n_cycles; /* only used inside the CPU loop */
+ uint64_t insn_counter;
+ BOOL power_down_flag;
+ int pending_exception; /* used during MMU exception handling */
+ target_ulong pending_tval;
+
+ /* CSRs */
+ target_ulong mstatus;
+ target_ulong mtvec;
+ target_ulong mscratch;
+ target_ulong mepc;
+ target_ulong mcause;
+ target_ulong mtval;
+ target_ulong mhartid; /* ro */
+ uint32_t misa;
+ uint32_t mie;
+ uint32_t mip;
+ uint32_t medeleg;
+ uint32_t mideleg;
+ uint32_t mcounteren;
+
+ target_ulong stvec;
+ target_ulong sscratch;
+ target_ulong sepc;
+ target_ulong scause;
+ target_ulong stval;
+#if MAX_XLEN == 32
+ uint32_t satp;
+#else
+ uint64_t satp; /* currently 64 bit physical addresses max */
+#endif
+ uint32_t scounteren;
+
+ target_ulong load_res; /* for atomic LR/SC */
+
+ PhysMemoryMap *mem_map;
+
+ TLBEntry tlb_read[TLB_SIZE];
+ TLBEntry tlb_write[TLB_SIZE];
+ TLBEntry tlb_code[TLB_SIZE];
+};
+
+#define target_read_slow glue(glue(riscv, MAX_XLEN), _read_slow)
+#define target_write_slow glue(glue(riscv, MAX_XLEN), _write_slow)
+
+DLL_PUBLIC int target_read_slow(RISCVCPUState *s, mem_uint_t *pval,
+ target_ulong addr, int size_log2);
+DLL_PUBLIC int target_write_slow(RISCVCPUState *s, target_ulong addr,
+ mem_uint_t val, int size_log2);
+
+/* return 0 if OK, != 0 if exception */
+#define TARGET_READ_WRITE(size, uint_type, size_log2) \
+static inline __exception int target_read_u ## size(RISCVCPUState *s, uint_type *pval, target_ulong addr) \
+{\
+ uint32_t tlb_idx;\
+ tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);\
+ if (likely(s->tlb_read[tlb_idx].vaddr == (addr & ~(PG_MASK & ~((size / 8) - 1))))) { \
+ *pval = *(uint_type *)(s->tlb_read[tlb_idx].mem_addend + (uintptr_t)addr);\
+ } else {\
+ mem_uint_t val;\
+ int ret;\
+ ret = target_read_slow(s, &val, addr, size_log2);\
+ if (ret)\
+ return ret;\
+ *pval = val;\
+ }\
+ return 0;\
+}\
+\
+static inline __exception int target_write_u ## size(RISCVCPUState *s, target_ulong addr,\
+ uint_type val) \
+{\
+ uint32_t tlb_idx;\
+ tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);\
+ if (likely(s->tlb_write[tlb_idx].vaddr == (addr & ~(PG_MASK & ~((size / 8) - 1))))) { \
+ *(uint_type *)(s->tlb_write[tlb_idx].mem_addend + (uintptr_t)addr) = val;\
+ return 0;\
+ } else {\
+ return target_write_slow(s, addr, val, size_log2);\
+ }\
+}
+
+TARGET_READ_WRITE(8, uint8_t, 0)
+TARGET_READ_WRITE(16, uint16_t, 1)
+TARGET_READ_WRITE(32, uint32_t, 2)
+#if MLEN >= 64
+TARGET_READ_WRITE(64, uint64_t, 3)
+#endif
+#if MLEN >= 128
+TARGET_READ_WRITE(128, uint128_t, 4)
+#endif
+
+#endif /* RISCV_CPU_PRIV_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_template.h b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_template.h
new file mode 100644
index 0000000..5092062
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_cpu_template.h
@@ -0,0 +1,1739 @@
+/*
+ * RISCV emulator
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#if XLEN == 32
+#define uintx_t uint32_t
+#define intx_t int32_t
+#elif XLEN == 64
+#define uintx_t uint64_t
+#define intx_t int64_t
+#elif XLEN == 128
+#define uintx_t uint128_t
+#define intx_t int128_t
+#else
+#error unsupported XLEN
+#endif
+
+static inline intx_t glue(div, XLEN)(intx_t a, intx_t b)
+{
+ if (b == 0) {
+ return -1;
+ } else if (a == ((intx_t)1 << (XLEN - 1)) && b == -1) {
+ return a;
+ } else {
+ return a / b;
+ }
+}
+
+static inline uintx_t glue(divu, XLEN)(uintx_t a, uintx_t b)
+{
+ if (b == 0) {
+ return -1;
+ } else {
+ return a / b;
+ }
+}
+
+static inline intx_t glue(rem, XLEN)(intx_t a, intx_t b)
+{
+ if (b == 0) {
+ return a;
+ } else if (a == ((intx_t)1 << (XLEN - 1)) && b == -1) {
+ return 0;
+ } else {
+ return a % b;
+ }
+}
+
+static inline uintx_t glue(remu, XLEN)(uintx_t a, uintx_t b)
+{
+ if (b == 0) {
+ return a;
+ } else {
+ return a % b;
+ }
+}
+
+#if XLEN == 32
+
+static inline uint32_t mulh32(int32_t a, int32_t b)
+{
+ return ((int64_t)a * (int64_t)b) >> 32;
+}
+
+static inline uint32_t mulhsu32(int32_t a, uint32_t b)
+{
+ return ((int64_t)a * (int64_t)b) >> 32;
+}
+
+static inline uint32_t mulhu32(uint32_t a, uint32_t b)
+{
+ return ((int64_t)a * (int64_t)b) >> 32;
+}
+
+#elif XLEN == 64 && defined(HAVE_INT128)
+
+static inline uint64_t mulh64(int64_t a, int64_t b)
+{
+ return ((int128_t)a * (int128_t)b) >> 64;
+}
+
+static inline uint64_t mulhsu64(int64_t a, uint64_t b)
+{
+ return ((int128_t)a * (int128_t)b) >> 64;
+}
+
+static inline uint64_t mulhu64(uint64_t a, uint64_t b)
+{
+ return ((int128_t)a * (int128_t)b) >> 64;
+}
+
+#else
+
+#if XLEN == 64
+#define UHALF uint32_t
+#define UHALF_LEN 32
+#elif XLEN == 128
+#define UHALF uint64_t
+#define UHALF_LEN 64
+#else
+#error unsupported XLEN
+#endif
+
+static uintx_t glue(mulhu, XLEN)(uintx_t a, uintx_t b)
+{
+ UHALF a0, a1, b0, b1, r2, r3;
+ uintx_t r00, r01, r10, r11, c;
+ a0 = a;
+ a1 = a >> UHALF_LEN;
+ b0 = b;
+ b1 = b >> UHALF_LEN;
+
+ r00 = (uintx_t)a0 * (uintx_t)b0;
+ r01 = (uintx_t)a0 * (uintx_t)b1;
+ r10 = (uintx_t)a1 * (uintx_t)b0;
+ r11 = (uintx_t)a1 * (uintx_t)b1;
+
+ // r0 = r00;
+ c = (r00 >> UHALF_LEN) + (UHALF)r01 + (UHALF)r10;
+ // r1 = c;
+ c = (c >> UHALF_LEN) + (r01 >> UHALF_LEN) + (r10 >> UHALF_LEN) + (UHALF)r11;
+ r2 = c;
+ r3 = (c >> UHALF_LEN) + (r11 >> UHALF_LEN);
+
+ // *plow = ((uintx_t)r1 << UHALF_LEN) | r0;
+ return ((uintx_t)r3 << UHALF_LEN) | r2;
+}
+
+#undef UHALF
+
+static inline uintx_t glue(mulh, XLEN)(intx_t a, intx_t b)
+{
+ uintx_t r1;
+ r1 = glue(mulhu, XLEN)(a, b);
+ if (a < 0)
+ r1 -= a;
+ if (b < 0)
+ r1 -= b;
+ return r1;
+}
+
+static inline uintx_t glue(mulhsu, XLEN)(intx_t a, uintx_t b)
+{
+ uintx_t r1;
+ r1 = glue(mulhu, XLEN)(a, b);
+ if (a < 0)
+ r1 -= a;
+ return r1;
+}
+
+#endif
+
+#define DUP2(F, n) F(n) F(n+1)
+#define DUP4(F, n) DUP2(F, n) DUP2(F, n + 2)
+#define DUP8(F, n) DUP4(F, n) DUP4(F, n + 4)
+#define DUP16(F, n) DUP8(F, n) DUP8(F, n + 8)
+#define DUP32(F, n) DUP16(F, n) DUP16(F, n + 16)
+
+#define C_QUADRANT(n) \
+ case n+(0 << 2): case n+(1 << 2): case n+(2 << 2): case n+(3 << 2): \
+ case n+(4 << 2): case n+(5 << 2): case n+(6 << 2): case n+(7 << 2): \
+ case n+(8 << 2): case n+(9 << 2): case n+(10 << 2): case n+(11 << 2): \
+ case n+(12 << 2): case n+(13 << 2): case n+(14 << 2): case n+(15 << 2): \
+ case n+(16 << 2): case n+(17 << 2): case n+(18 << 2): case n+(19 << 2): \
+ case n+(20 << 2): case n+(21 << 2): case n+(22 << 2): case n+(23 << 2): \
+ case n+(24 << 2): case n+(25 << 2): case n+(26 << 2): case n+(27 << 2): \
+ case n+(28 << 2): case n+(29 << 2): case n+(30 << 2): case n+(31 << 2):
+
+#define GET_PC() (target_ulong)((uintptr_t)code_ptr + code_to_pc_addend)
+#define GET_INSN_COUNTER() (insn_counter_addend - s->n_cycles)
+
+#define C_NEXT_INSN code_ptr += 2; break
+#define NEXT_INSN code_ptr += 4; break
+#define JUMP_INSN do { \
+ code_ptr = NULL; \
+ code_end = NULL; \
+ code_to_pc_addend = s->pc; \
+ goto jump_insn; \
+ } while (0)
+
+static void no_inline glue(riscv_cpu_interp_x, XLEN)(RISCVCPUState *s,
+ int n_cycles1)
+{
+ uint32_t opcode, insn, rd, rs1, rs2, funct3;
+ int32_t imm, cond, err;
+ target_ulong addr, val, val2;
+#ifndef USE_GLOBAL_VARIABLES
+ uint8_t *code_ptr, *code_end;
+ target_ulong code_to_pc_addend;
+#endif
+ uint64_t insn_counter_addend;
+#if FLEN > 0
+ uint32_t rs3;
+ int32_t rm;
+#endif
+
+ if (n_cycles1 == 0)
+ return;
+ insn_counter_addend = s->insn_counter + n_cycles1;
+ s->n_cycles = n_cycles1;
+
+ /* check pending interrupts */
+ if (unlikely((s->mip & s->mie) != 0)) {
+ if (raise_interrupt(s)) {
+ s->n_cycles--;
+ goto done_interp;
+ }
+ }
+
+ s->pending_exception = -1;
+ /* Note: we assume NULL is represented as a zero number */
+ code_ptr = NULL;
+ code_end = NULL;
+ code_to_pc_addend = s->pc;
+
+ /* we use a single execution loop to keep a simple control flow
+ for emscripten */
+ for(;;) {
+ if (unlikely(code_ptr >= code_end)) {
+ uint32_t tlb_idx;
+ uint16_t insn_high;
+ target_ulong addr;
+ uint8_t *ptr;
+
+ s->pc = GET_PC();
+ /* we test n_cycles only between blocks so that timer
+ interrupts only happen between the blocks. It is
+ important to reduce the translated code size. */
+ if (unlikely(s->n_cycles <= 0))
+ goto the_end;
+
+ /* check pending interrupts */
+ if (unlikely((s->mip & s->mie) != 0)) {
+ if (raise_interrupt(s)) {
+ s->n_cycles--;
+ goto the_end;
+ }
+ }
+
+ addr = s->pc;
+ tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);
+ if (likely(s->tlb_code[tlb_idx].vaddr == (addr & ~PG_MASK))) {
+ /* TLB match */
+ ptr = (uint8_t *)(s->tlb_code[tlb_idx].mem_addend +
+ (uintptr_t)addr);
+ } else {
+ if (unlikely(target_read_insn_slow(s, &ptr, addr)))
+ goto mmu_exception;
+ }
+ code_ptr = ptr;
+ code_end = ptr + (PG_MASK - 1 - (addr & PG_MASK));
+ code_to_pc_addend = addr - (uintptr_t)code_ptr;
+ if (unlikely(code_ptr >= code_end)) {
+ /* instruction is potentially half way between two
+ pages ? */
+ insn = *(uint16_t *)code_ptr;
+ if ((insn & 3) == 3) {
+ /* instruction is half way between two pages */
+ if (unlikely(target_read_insn_u16(s, &insn_high, addr + 2)))
+ goto mmu_exception;
+ insn |= insn_high << 16;
+ }
+ } else {
+ insn = get_insn32(code_ptr);
+ }
+ } else {
+ /* fast path */
+ insn = get_insn32(code_ptr);
+ }
+ s->n_cycles--;
+#if 0
+ if (1) {
+#ifdef CONFIG_LOGFILE
+ log_printf("pc=0x"); fprint_target_ulong(log_file, GET_PC()); log_printf(" insn=%08x\n", insn);
+ fflush(log_file);
+#else
+ printf("pc=0x"); print_target_ulong(GET_PC()); printf(" insn=%08x\n", insn);
+ // dump_regs(s);
+#endif
+ }
+#endif
+ opcode = insn & 0x7f;
+ rd = (insn >> 7) & 0x1f;
+ rs1 = (insn >> 15) & 0x1f;
+ rs2 = (insn >> 20) & 0x1f;
+ switch(opcode) {
+#ifdef CONFIG_EXT_C
+ C_QUADRANT(0)
+ funct3 = (insn >> 13) & 7;
+ rd = ((insn >> 2) & 7) | 8;
+ switch(funct3) {
+ case 0: /* c.addi4spn */
+ imm = get_field1(insn, 11, 4, 5) |
+ get_field1(insn, 7, 6, 9) |
+ get_field1(insn, 6, 2, 2) |
+ get_field1(insn, 5, 3, 3);
+ if (imm == 0)
+ goto illegal_insn;
+ s->reg[rd] = (intx_t)(s->reg[2] + imm);
+ break;
+#if XLEN >= 128
+ case 1: /* c.lq */
+ imm = get_field1(insn, 11, 4, 5) |
+ get_field1(insn, 10, 8, 8) |
+ get_field1(insn, 5, 6, 7);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ if (target_read_u128(s, &val, addr))
+ goto mmu_exception;
+ s->reg[rd] = val;
+ break;
+#elif FLEN >= 64
+ case 1: /* c.fld */
+ {
+ uint64_t rval;
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 5, 6, 7);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ if (target_read_u64(s, &rval, addr))
+ goto mmu_exception;
+ s->fp_reg[rd] = rval | F64_HIGH;
+ s->fs = 3;
+ }
+ break;
+#endif
+ case 2: /* c.lw */
+ {
+ uint32_t rval;
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 6, 2, 2) |
+ get_field1(insn, 5, 6, 6);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ if (target_read_u32(s, &rval, addr))
+ goto mmu_exception;
+ s->reg[rd] = (int32_t)rval;
+ }
+ break;
+#if XLEN >= 64
+ case 3: /* c.ld */
+ {
+ uint64_t rval;
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 5, 6, 7);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ if (target_read_u64(s, &rval, addr))
+ goto mmu_exception;
+ s->reg[rd] = (int64_t)rval;
+ }
+ break;
+#elif FLEN >= 32
+ case 3: /* c.flw */
+ {
+ uint32_t rval;
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 6, 2, 2) |
+ get_field1(insn, 5, 6, 6);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ if (target_read_u32(s, &rval, addr))
+ goto mmu_exception;
+ s->fp_reg[rd] = rval | F32_HIGH;
+ s->fs = 3;
+ }
+ break;
+#endif
+#if XLEN >= 128
+ case 5: /* c.sq */
+ imm = get_field1(insn, 11, 4, 5) |
+ get_field1(insn, 10, 8, 8) |
+ get_field1(insn, 5, 6, 7);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ val = s->reg[rd];
+ if (target_write_u128(s, addr, val))
+ goto mmu_exception;
+ break;
+#elif FLEN >= 64
+ case 5: /* c.fsd */
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 5, 6, 7);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ if (target_write_u64(s, addr, s->fp_reg[rd]))
+ goto mmu_exception;
+ break;
+#endif
+ case 6: /* c.sw */
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 6, 2, 2) |
+ get_field1(insn, 5, 6, 6);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ val = s->reg[rd];
+ if (target_write_u32(s, addr, val))
+ goto mmu_exception;
+ break;
+#if XLEN >= 64
+ case 7: /* c.sd */
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 5, 6, 7);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ val = s->reg[rd];
+ if (target_write_u64(s, addr, val))
+ goto mmu_exception;
+ break;
+#elif FLEN >= 32
+ case 7: /* c.fsw */
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 6, 2, 2) |
+ get_field1(insn, 5, 6, 6);
+ rs1 = ((insn >> 7) & 7) | 8;
+ addr = (intx_t)(s->reg[rs1] + imm);
+ if (target_write_u32(s, addr, s->fp_reg[rd]))
+ goto mmu_exception;
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ C_NEXT_INSN;
+ C_QUADRANT(1)
+ funct3 = (insn >> 13) & 7;
+ switch(funct3) {
+ case 0: /* c.addi/c.nop */
+ if (rd != 0) {
+ imm = sext(get_field1(insn, 12, 5, 5) |
+ get_field1(insn, 2, 0, 4), 6);
+ s->reg[rd] = (intx_t)(s->reg[rd] + imm);
+ }
+ break;
+#if XLEN == 32
+ case 1: /* c.jal */
+ imm = sext(get_field1(insn, 12, 11, 11) |
+ get_field1(insn, 11, 4, 4) |
+ get_field1(insn, 9, 8, 9) |
+ get_field1(insn, 8, 10, 10) |
+ get_field1(insn, 7, 6, 6) |
+ get_field1(insn, 6, 7, 7) |
+ get_field1(insn, 3, 1, 3) |
+ get_field1(insn, 2, 5, 5), 12);
+ s->reg[1] = GET_PC() + 2;
+ s->pc = (intx_t)(GET_PC() + imm);
+ JUMP_INSN;
+#else
+ case 1: /* c.addiw */
+ if (rd != 0) {
+ imm = sext(get_field1(insn, 12, 5, 5) |
+ get_field1(insn, 2, 0, 4), 6);
+ s->reg[rd] = (int32_t)(s->reg[rd] + imm);
+ }
+ break;
+#endif
+ case 2: /* c.li */
+ if (rd != 0) {
+ imm = sext(get_field1(insn, 12, 5, 5) |
+ get_field1(insn, 2, 0, 4), 6);
+ s->reg[rd] = imm;
+ }
+ break;
+ case 3:
+ if (rd == 2) {
+ /* c.addi16sp */
+ imm = sext(get_field1(insn, 12, 9, 9) |
+ get_field1(insn, 6, 4, 4) |
+ get_field1(insn, 5, 6, 6) |
+ get_field1(insn, 3, 7, 8) |
+ get_field1(insn, 2, 5, 5), 10);
+ if (imm == 0)
+ goto illegal_insn;
+ s->reg[2] = (intx_t)(s->reg[2] + imm);
+ } else if (rd != 0) {
+ /* c.lui */
+ imm = sext(get_field1(insn, 12, 17, 17) |
+ get_field1(insn, 2, 12, 16), 18);
+ s->reg[rd] = imm;
+ }
+ break;
+ case 4:
+ funct3 = (insn >> 10) & 3;
+ rd = ((insn >> 7) & 7) | 8;
+ switch(funct3) {
+ case 0: /* c.srli */
+ case 1: /* c.srai */
+ imm = get_field1(insn, 12, 5, 5) |
+ get_field1(insn, 2, 0, 4);
+#if XLEN == 32
+ if (imm & 0x20)
+ goto illegal_insn;
+#elif XLEN == 128
+ if (imm == 0)
+ imm = 64;
+ else if (imm >= 32)
+ imm = 128 - imm;
+#endif
+ if (funct3 == 0)
+ s->reg[rd] = (intx_t)((uintx_t)s->reg[rd] >> imm);
+ else
+ s->reg[rd] = (intx_t)s->reg[rd] >> imm;
+
+ break;
+ case 2: /* c.andi */
+ imm = sext(get_field1(insn, 12, 5, 5) |
+ get_field1(insn, 2, 0, 4), 6);
+ s->reg[rd] &= imm;
+ break;
+ case 3:
+ rs2 = ((insn >> 2) & 7) | 8;
+ funct3 = ((insn >> 5) & 3) | ((insn >> (12 - 2)) & 4);
+ switch(funct3) {
+ case 0: /* c.sub */
+ s->reg[rd] = (intx_t)(s->reg[rd] - s->reg[rs2]);
+ break;
+ case 1: /* c.xor */
+ s->reg[rd] = s->reg[rd] ^ s->reg[rs2];
+ break;
+ case 2: /* c.or */
+ s->reg[rd] = s->reg[rd] | s->reg[rs2];
+ break;
+ case 3: /* c.and */
+ s->reg[rd] = s->reg[rd] & s->reg[rs2];
+ break;
+#if XLEN >= 64
+ case 4: /* c.subw */
+ s->reg[rd] = (int32_t)(s->reg[rd] - s->reg[rs2]);
+ break;
+ case 5: /* c.addw */
+ s->reg[rd] = (int32_t)(s->reg[rd] + s->reg[rs2]);
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ break;
+ }
+ break;
+ case 5: /* c.j */
+ imm = sext(get_field1(insn, 12, 11, 11) |
+ get_field1(insn, 11, 4, 4) |
+ get_field1(insn, 9, 8, 9) |
+ get_field1(insn, 8, 10, 10) |
+ get_field1(insn, 7, 6, 6) |
+ get_field1(insn, 6, 7, 7) |
+ get_field1(insn, 3, 1, 3) |
+ get_field1(insn, 2, 5, 5), 12);
+ s->pc = (intx_t)(GET_PC() + imm);
+ JUMP_INSN;
+ case 6: /* c.beqz */
+ rs1 = ((insn >> 7) & 7) | 8;
+ imm = sext(get_field1(insn, 12, 8, 8) |
+ get_field1(insn, 10, 3, 4) |
+ get_field1(insn, 5, 6, 7) |
+ get_field1(insn, 3, 1, 2) |
+ get_field1(insn, 2, 5, 5), 9);
+ if (s->reg[rs1] == 0) {
+ s->pc = (intx_t)(GET_PC() + imm);
+ JUMP_INSN;
+ }
+ break;
+ case 7: /* c.bnez */
+ rs1 = ((insn >> 7) & 7) | 8;
+ imm = sext(get_field1(insn, 12, 8, 8) |
+ get_field1(insn, 10, 3, 4) |
+ get_field1(insn, 5, 6, 7) |
+ get_field1(insn, 3, 1, 2) |
+ get_field1(insn, 2, 5, 5), 9);
+ if (s->reg[rs1] != 0) {
+ s->pc = (intx_t)(GET_PC() + imm);
+ JUMP_INSN;
+ }
+ break;
+ default:
+ goto illegal_insn;
+ }
+ C_NEXT_INSN;
+ C_QUADRANT(2)
+ funct3 = (insn >> 13) & 7;
+ rs2 = (insn >> 2) & 0x1f;
+ switch(funct3) {
+ case 0: /* c.slli */
+ imm = get_field1(insn, 12, 5, 5) | rs2;
+#if XLEN == 32
+ if (imm & 0x20)
+ goto illegal_insn;
+#elif XLEN == 128
+ if (imm == 0)
+ imm = 64;
+#endif
+ if (rd != 0)
+ s->reg[rd] = (intx_t)(s->reg[rd] << imm);
+ break;
+#if XLEN == 128
+ case 1: /* c.lqsp */
+ imm = get_field1(insn, 12, 5, 5) |
+ (rs2 & (1 << 4)) |
+ get_field1(insn, 2, 6, 9);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_read_u128(s, &val, addr))
+ goto mmu_exception;
+ if (rd != 0)
+ s->reg[rd] = val;
+ break;
+#elif FLEN >= 64
+ case 1: /* c.fldsp */
+ {
+ uint64_t rval;
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = get_field1(insn, 12, 5, 5) |
+ (rs2 & (3 << 3)) |
+ get_field1(insn, 2, 6, 8);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_read_u64(s, &rval, addr))
+ goto mmu_exception;
+ s->fp_reg[rd] = rval | F64_HIGH;
+ s->fs = 3;
+ }
+ break;
+#endif
+ case 2: /* c.lwsp */
+ {
+ uint32_t rval;
+ imm = get_field1(insn, 12, 5, 5) |
+ (rs2 & (7 << 2)) |
+ get_field1(insn, 2, 6, 7);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_read_u32(s, &rval, addr))
+ goto mmu_exception;
+ if (rd != 0)
+ s->reg[rd] = (int32_t)rval;
+ }
+ break;
+#if XLEN >= 64
+ case 3: /* c.ldsp */
+ {
+ uint64_t rval;
+ imm = get_field1(insn, 12, 5, 5) |
+ (rs2 & (3 << 3)) |
+ get_field1(insn, 2, 6, 8);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_read_u64(s, &rval, addr))
+ goto mmu_exception;
+ if (rd != 0)
+ s->reg[rd] = (int64_t)rval;
+ }
+ break;
+#elif FLEN >= 32
+ case 3: /* c.flwsp */
+ {
+ uint32_t rval;
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = get_field1(insn, 12, 5, 5) |
+ (rs2 & (7 << 2)) |
+ get_field1(insn, 2, 6, 7);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_read_u32(s, &rval, addr))
+ goto mmu_exception;
+ s->fp_reg[rd] = rval | F32_HIGH;
+ s->fs = 3;
+ }
+ break;
+#endif
+ case 4:
+ if (((insn >> 12) & 1) == 0) {
+ if (rs2 == 0) {
+ /* c.jr */
+ if (rd == 0)
+ goto illegal_insn;
+ s->pc = s->reg[rd] & ~1;
+ JUMP_INSN;
+ } else {
+ /* c.mv */
+ if (rd != 0)
+ s->reg[rd] = s->reg[rs2];
+ }
+ } else {
+ if (rs2 == 0) {
+ if (rd == 0) {
+ /* c.ebreak */
+ s->pending_exception = CAUSE_BREAKPOINT;
+ goto exception;
+ } else {
+ /* c.jalr */
+ val = GET_PC() + 2;
+ s->pc = s->reg[rd] & ~1;
+ s->reg[1] = val;
+ JUMP_INSN;
+ }
+ } else {
+ if (rd != 0) {
+ s->reg[rd] = (intx_t)(s->reg[rd] + s->reg[rs2]);
+ }
+ }
+ }
+ break;
+#if XLEN == 128
+ case 5: /* c.sqsp */
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 7, 6, 8);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_write_u128(s, addr, s->reg[rs2]))
+ goto mmu_exception;
+ break;
+#elif FLEN >= 64
+ case 5: /* c.fsdsp */
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 7, 6, 8);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_write_u64(s, addr, s->fp_reg[rs2]))
+ goto mmu_exception;
+ break;
+#endif
+ case 6: /* c.swsp */
+ imm = get_field1(insn, 9, 2, 5) |
+ get_field1(insn, 7, 6, 7);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_write_u32(s, addr, s->reg[rs2]))
+ goto mmu_exception;
+ break;
+#if XLEN >= 64
+ case 7: /* c.sdsp */
+ imm = get_field1(insn, 10, 3, 5) |
+ get_field1(insn, 7, 6, 8);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_write_u64(s, addr, s->reg[rs2]))
+ goto mmu_exception;
+ break;
+#elif FLEN >= 32
+ case 7: /* c.swsp */
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = get_field1(insn, 9, 2, 5) |
+ get_field1(insn, 7, 6, 7);
+ addr = (intx_t)(s->reg[2] + imm);
+ if (target_write_u32(s, addr, s->fp_reg[rs2]))
+ goto mmu_exception;
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ C_NEXT_INSN;
+#endif /* CONFIG_EXT_C */
+
+ case 0x37: /* lui */
+ if (rd != 0)
+ s->reg[rd] = (int32_t)(insn & 0xfffff000);
+ NEXT_INSN;
+ case 0x17: /* auipc */
+ if (rd != 0)
+ s->reg[rd] = (intx_t)(GET_PC() + (int32_t)(insn & 0xfffff000));
+ NEXT_INSN;
+ case 0x6f: /* jal */
+ imm = ((insn >> (31 - 20)) & (1 << 20)) |
+ ((insn >> (21 - 1)) & 0x7fe) |
+ ((insn >> (20 - 11)) & (1 << 11)) |
+ (insn & 0xff000);
+ imm = (imm << 11) >> 11;
+ if (rd != 0)
+ s->reg[rd] = GET_PC() + 4;
+ s->pc = (intx_t)(GET_PC() + imm);
+ JUMP_INSN;
+ case 0x67: /* jalr */
+ imm = (int32_t)insn >> 20;
+ val = GET_PC() + 4;
+ s->pc = (intx_t)(s->reg[rs1] + imm) & ~1;
+ if (rd != 0)
+ s->reg[rd] = val;
+ JUMP_INSN;
+ case 0x63:
+ funct3 = (insn >> 12) & 7;
+ switch(funct3 >> 1) {
+ case 0: /* beq/bne */
+ cond = (s->reg[rs1] == s->reg[rs2]);
+ break;
+ case 2: /* blt/bge */
+ cond = ((target_long)s->reg[rs1] < (target_long)s->reg[rs2]);
+ break;
+ case 3: /* bltu/bgeu */
+ cond = (s->reg[rs1] < s->reg[rs2]);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ cond ^= (funct3 & 1);
+ if (cond) {
+ imm = ((insn >> (31 - 12)) & (1 << 12)) |
+ ((insn >> (25 - 5)) & 0x7e0) |
+ ((insn >> (8 - 1)) & 0x1e) |
+ ((insn << (11 - 7)) & (1 << 11));
+ imm = (imm << 19) >> 19;
+ s->pc = (intx_t)(GET_PC() + imm);
+ JUMP_INSN;
+ }
+ NEXT_INSN;
+ case 0x03: /* load */
+ funct3 = (insn >> 12) & 7;
+ imm = (int32_t)insn >> 20;
+ addr = s->reg[rs1] + imm;
+ switch(funct3) {
+ case 0: /* lb */
+ {
+ uint8_t rval;
+ if (target_read_u8(s, &rval, addr))
+ goto mmu_exception;
+ val = (int8_t)rval;
+ }
+ break;
+ case 1: /* lh */
+ {
+ uint16_t rval;
+ if (target_read_u16(s, &rval, addr))
+ goto mmu_exception;
+ val = (int16_t)rval;
+ }
+ break;
+ case 2: /* lw */
+ {
+ uint32_t rval;
+ if (target_read_u32(s, &rval, addr))
+ goto mmu_exception;
+ val = (int32_t)rval;
+ }
+ break;
+ case 4: /* lbu */
+ {
+ uint8_t rval;
+ if (target_read_u8(s, &rval, addr))
+ goto mmu_exception;
+ val = rval;
+ }
+ break;
+ case 5: /* lhu */
+ {
+ uint16_t rval;
+ if (target_read_u16(s, &rval, addr))
+ goto mmu_exception;
+ val = rval;
+ }
+ break;
+#if XLEN >= 64
+ case 3: /* ld */
+ {
+ uint64_t rval;
+ if (target_read_u64(s, &rval, addr))
+ goto mmu_exception;
+ val = (int64_t)rval;
+ }
+ break;
+ case 6: /* lwu */
+ {
+ uint32_t rval;
+ if (target_read_u32(s, &rval, addr))
+ goto mmu_exception;
+ val = rval;
+ }
+ break;
+#endif
+#if XLEN >= 128
+ case 7: /* ldu */
+ {
+ uint64_t rval;
+ if (target_read_u64(s, &rval, addr))
+ goto mmu_exception;
+ val = rval;
+ }
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ NEXT_INSN;
+ case 0x23: /* store */
+ funct3 = (insn >> 12) & 7;
+ imm = rd | ((insn >> (25 - 5)) & 0xfe0);
+ imm = (imm << 20) >> 20;
+ addr = s->reg[rs1] + imm;
+ val = s->reg[rs2];
+ switch(funct3) {
+ case 0: /* sb */
+ if (target_write_u8(s, addr, val))
+ goto mmu_exception;
+ break;
+ case 1: /* sh */
+ if (target_write_u16(s, addr, val))
+ goto mmu_exception;
+ break;
+ case 2: /* sw */
+ if (target_write_u32(s, addr, val))
+ goto mmu_exception;
+ break;
+#if XLEN >= 64
+ case 3: /* sd */
+ if (target_write_u64(s, addr, val))
+ goto mmu_exception;
+ break;
+#endif
+#if XLEN >= 128
+ case 4: /* sq */
+ if (target_write_u128(s, addr, val))
+ goto mmu_exception;
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ NEXT_INSN;
+ case 0x13:
+ funct3 = (insn >> 12) & 7;
+ imm = (int32_t)insn >> 20;
+ switch(funct3) {
+ case 0: /* addi */
+ val = (intx_t)(s->reg[rs1] + imm);
+ break;
+ case 1: /* slli */
+ if ((imm & ~(XLEN - 1)) != 0)
+ goto illegal_insn;
+ val = (intx_t)(s->reg[rs1] << (imm & (XLEN - 1)));
+ break;
+ case 2: /* slti */
+ val = (target_long)s->reg[rs1] < (target_long)imm;
+ break;
+ case 3: /* sltiu */
+ val = s->reg[rs1] < (target_ulong)imm;
+ break;
+ case 4: /* xori */
+ val = s->reg[rs1] ^ imm;
+ break;
+ case 5: /* srli/srai */
+ if ((imm & ~((XLEN - 1) | 0x400)) != 0)
+ goto illegal_insn;
+ if (imm & 0x400)
+ val = (intx_t)s->reg[rs1] >> (imm & (XLEN - 1));
+ else
+ val = (intx_t)((uintx_t)s->reg[rs1] >> (imm & (XLEN - 1)));
+ break;
+ case 6: /* ori */
+ val = s->reg[rs1] | imm;
+ break;
+ default:
+ case 7: /* andi */
+ val = s->reg[rs1] & imm;
+ break;
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ NEXT_INSN;
+#if XLEN >= 64
+ case 0x1b:/* OP-IMM-32 */
+ funct3 = (insn >> 12) & 7;
+ imm = (int32_t)insn >> 20;
+ val = s->reg[rs1];
+ switch(funct3) {
+ case 0: /* addiw */
+ val = (int32_t)(val + imm);
+ break;
+ case 1: /* slliw */
+ if ((imm & ~31) != 0)
+ goto illegal_insn;
+ val = (int32_t)(val << (imm & 31));
+ break;
+ case 5: /* srliw/sraiw */
+ if ((imm & ~(31 | 0x400)) != 0)
+ goto illegal_insn;
+ if (imm & 0x400)
+ val = (int32_t)val >> (imm & 31);
+ else
+ val = (int32_t)((uint32_t)val >> (imm & 31));
+ break;
+ default:
+ goto illegal_insn;
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ NEXT_INSN;
+#endif
+#if XLEN >= 128
+ case 0x5b: /* OP-IMM-64 */
+ funct3 = (insn >> 12) & 7;
+ imm = (int32_t)insn >> 20;
+ val = s->reg[rs1];
+ switch(funct3) {
+ case 0: /* addid */
+ val = (int64_t)(val + imm);
+ break;
+ case 1: /* sllid */
+ if ((imm & ~63) != 0)
+ goto illegal_insn;
+ val = (int64_t)(val << (imm & 63));
+ break;
+ case 5: /* srlid/sraid */
+ if ((imm & ~(63 | 0x400)) != 0)
+ goto illegal_insn;
+ if (imm & 0x400)
+ val = (int64_t)val >> (imm & 63);
+ else
+ val = (int64_t)((uint64_t)val >> (imm & 63));
+ break;
+ default:
+ goto illegal_insn;
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ NEXT_INSN;
+#endif
+ case 0x33:
+ imm = insn >> 25;
+ val = s->reg[rs1];
+ val2 = s->reg[rs2];
+ if (imm == 1) {
+ funct3 = (insn >> 12) & 7;
+ switch(funct3) {
+ case 0: /* mul */
+ val = (intx_t)((intx_t)val * (intx_t)val2);
+ break;
+ case 1: /* mulh */
+ val = (intx_t)glue(mulh, XLEN)(val, val2);
+ break;
+ case 2:/* mulhsu */
+ val = (intx_t)glue(mulhsu, XLEN)(val, val2);
+ break;
+ case 3:/* mulhu */
+ val = (intx_t)glue(mulhu, XLEN)(val, val2);
+ break;
+ case 4:/* div */
+ val = glue(div, XLEN)(val, val2);
+ break;
+ case 5:/* divu */
+ val = (intx_t)glue(divu, XLEN)(val, val2);
+ break;
+ case 6:/* rem */
+ val = glue(rem, XLEN)(val, val2);
+ break;
+ case 7:/* remu */
+ val = (intx_t)glue(remu, XLEN)(val, val2);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ } else {
+ if (imm & ~0x20)
+ goto illegal_insn;
+ funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3));
+ switch(funct3) {
+ case 0: /* add */
+ val = (intx_t)(val + val2);
+ break;
+ case 0 | 8: /* sub */
+ val = (intx_t)(val - val2);
+ break;
+ case 1: /* sll */
+ val = (intx_t)(val << (val2 & (XLEN - 1)));
+ break;
+ case 2: /* slt */
+ val = (target_long)val < (target_long)val2;
+ break;
+ case 3: /* sltu */
+ val = val < val2;
+ break;
+ case 4: /* xor */
+ val = val ^ val2;
+ break;
+ case 5: /* srl */
+ val = (intx_t)((uintx_t)val >> (val2 & (XLEN - 1)));
+ break;
+ case 5 | 8: /* sra */
+ val = (intx_t)val >> (val2 & (XLEN - 1));
+ break;
+ case 6: /* or */
+ val = val | val2;
+ break;
+ case 7: /* and */
+ val = val & val2;
+ break;
+ default:
+ goto illegal_insn;
+ }
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ NEXT_INSN;
+#if XLEN >= 64
+ case 0x3b: /* OP-32 */
+ imm = insn >> 25;
+ val = s->reg[rs1];
+ val2 = s->reg[rs2];
+ if (imm == 1) {
+ funct3 = (insn >> 12) & 7;
+ switch(funct3) {
+ case 0: /* mulw */
+ val = (int32_t)((int32_t)val * (int32_t)val2);
+ break;
+ case 4:/* divw */
+ val = div32(val, val2);
+ break;
+ case 5:/* divuw */
+ val = (int32_t)divu32(val, val2);
+ break;
+ case 6:/* remw */
+ val = rem32(val, val2);
+ break;
+ case 7:/* remuw */
+ val = (int32_t)remu32(val, val2);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ } else {
+ if (imm & ~0x20)
+ goto illegal_insn;
+ funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3));
+ switch(funct3) {
+ case 0: /* addw */
+ val = (int32_t)(val + val2);
+ break;
+ case 0 | 8: /* subw */
+ val = (int32_t)(val - val2);
+ break;
+ case 1: /* sllw */
+ val = (int32_t)((uint32_t)val << (val2 & 31));
+ break;
+ case 5: /* srlw */
+ val = (int32_t)((uint32_t)val >> (val2 & 31));
+ break;
+ case 5 | 8: /* sraw */
+ val = (int32_t)val >> (val2 & 31);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ NEXT_INSN;
+#endif
+#if XLEN >= 128
+ case 0x7b: /* OP-64 */
+ imm = insn >> 25;
+ val = s->reg[rs1];
+ val2 = s->reg[rs2];
+ if (imm == 1) {
+ funct3 = (insn >> 12) & 7;
+ switch(funct3) {
+ case 0: /* muld */
+ val = (int64_t)((int64_t)val * (int64_t)val2);
+ break;
+ case 4:/* divd */
+ val = div64(val, val2);
+ break;
+ case 5:/* divud */
+ val = (int64_t)divu64(val, val2);
+ break;
+ case 6:/* remd */
+ val = rem64(val, val2);
+ break;
+ case 7:/* remud */
+ val = (int64_t)remu64(val, val2);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ } else {
+ if (imm & ~0x20)
+ goto illegal_insn;
+ funct3 = ((insn >> 12) & 7) | ((insn >> (30 - 3)) & (1 << 3));
+ switch(funct3) {
+ case 0: /* addd */
+ val = (int64_t)(val + val2);
+ break;
+ case 0 | 8: /* subd */
+ val = (int64_t)(val - val2);
+ break;
+ case 1: /* slld */
+ val = (int64_t)((uint64_t)val << (val2 & 63));
+ break;
+ case 5: /* srld */
+ val = (int64_t)((uint64_t)val >> (val2 & 63));
+ break;
+ case 5 | 8: /* srad */
+ val = (int64_t)val >> (val2 & 63);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ NEXT_INSN;
+#endif
+ case 0x73:
+ funct3 = (insn >> 12) & 7;
+ imm = insn >> 20;
+ if (funct3 & 4)
+ val = rs1;
+ else
+ val = s->reg[rs1];
+ funct3 &= 3;
+ switch(funct3) {
+ case 1: /* csrrw */
+ s->insn_counter = GET_INSN_COUNTER();
+ if (csr_read(s, &val2, imm, TRUE))
+ goto illegal_insn;
+ val2 = (intx_t)val2;
+ err = csr_write(s, imm, val);
+ if (err < 0)
+ goto illegal_insn;
+ if (rd != 0)
+ s->reg[rd] = val2;
+ if (err > 0) {
+ s->pc = GET_PC() + 4;
+ if (err == 2)
+ JUMP_INSN;
+ else
+ goto done_interp;
+ }
+ break;
+ case 2: /* csrrs */
+ case 3: /* csrrc */
+ s->insn_counter = GET_INSN_COUNTER();
+ if (csr_read(s, &val2, imm, (rs1 != 0)))
+ goto illegal_insn;
+ val2 = (intx_t)val2;
+ if (rs1 != 0) {
+ if (funct3 == 2)
+ val = val2 | val;
+ else
+ val = val2 & ~val;
+ err = csr_write(s, imm, val);
+ if (err < 0)
+ goto illegal_insn;
+ } else {
+ err = 0;
+ }
+ if (rd != 0)
+ s->reg[rd] = val2;
+ if (err > 0) {
+ s->pc = GET_PC() + 4;
+ if (err == 2)
+ JUMP_INSN;
+ else
+ goto done_interp;
+ }
+ break;
+ case 0:
+ switch(imm) {
+ case 0x000: /* ecall */
+ if (insn & 0x000fff80)
+ goto illegal_insn;
+ s->pending_exception = CAUSE_USER_ECALL + s->priv;
+ goto exception;
+ case 0x001: /* ebreak */
+ if (insn & 0x000fff80)
+ goto illegal_insn;
+ s->pending_exception = CAUSE_BREAKPOINT;
+ goto exception;
+ case 0x102: /* sret */
+ {
+ if (insn & 0x000fff80)
+ goto illegal_insn;
+ if (s->priv < PRV_S)
+ goto illegal_insn;
+ s->pc = GET_PC();
+ handle_sret(s);
+ goto done_interp;
+ }
+ break;
+ case 0x302: /* mret */
+ {
+ if (insn & 0x000fff80)
+ goto illegal_insn;
+ if (s->priv < PRV_M)
+ goto illegal_insn;
+ s->pc = GET_PC();
+ handle_mret(s);
+ goto done_interp;
+ }
+ break;
+ case 0x105: /* wfi */
+ if (insn & 0x00007f80)
+ goto illegal_insn;
+ if (s->priv == PRV_U)
+ goto illegal_insn;
+ /* go to power down if no enabled interrupts are
+ pending */
+ if ((s->mip & s->mie) == 0) {
+ s->power_down_flag = TRUE;
+ s->pc = GET_PC() + 4;
+ goto done_interp;
+ }
+ break;
+ default:
+ if ((imm >> 5) == 0x09) {
+ /* sfence.vma */
+ if (insn & 0x00007f80)
+ goto illegal_insn;
+ if (s->priv == PRV_U)
+ goto illegal_insn;
+ if (rs1 == 0) {
+ tlb_flush_all(s);
+ } else {
+ tlb_flush_vaddr(s, s->reg[rs1]);
+ }
+ /* the current code TLB may have been flushed */
+ s->pc = GET_PC() + 4;
+ JUMP_INSN;
+ } else {
+ goto illegal_insn;
+ }
+ break;
+ }
+ break;
+ default:
+ goto illegal_insn;
+ }
+ NEXT_INSN;
+ case 0x0f: /* misc-mem */
+ funct3 = (insn >> 12) & 7;
+ switch(funct3) {
+ case 0: /* fence */
+ if (insn & 0xf00fff80)
+ goto illegal_insn;
+ break;
+ case 1: /* fence.i */
+ if (insn != 0x0000100f)
+ goto illegal_insn;
+ break;
+#if XLEN >= 128
+ case 2: /* lq */
+ imm = (int32_t)insn >> 20;
+ addr = s->reg[rs1] + imm;
+ if (target_read_u128(s, &val, addr))
+ goto mmu_exception;
+ if (rd != 0)
+ s->reg[rd] = val;
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ NEXT_INSN;
+ case 0x2f:
+ funct3 = (insn >> 12) & 7;
+#define OP_A(size) \
+ { \
+ uint ## size ##_t rval; \
+ \
+ addr = s->reg[rs1]; \
+ funct3 = insn >> 27; \
+ switch(funct3) { \
+ case 2: /* lr.w */ \
+ if (rs2 != 0) \
+ goto illegal_insn; \
+ if (target_read_u ## size(s, &rval, addr)) \
+ goto mmu_exception; \
+ val = (int## size ## _t)rval; \
+ s->load_res = addr; \
+ break; \
+ case 3: /* sc.w */ \
+ if (s->load_res == addr) { \
+ if (target_write_u ## size(s, addr, s->reg[rs2])) \
+ goto mmu_exception; \
+ val = 0; \
+ } else { \
+ val = 1; \
+ } \
+ break; \
+ case 1: /* amiswap.w */ \
+ case 0: /* amoadd.w */ \
+ case 4: /* amoxor.w */ \
+ case 0xc: /* amoand.w */ \
+ case 0x8: /* amoor.w */ \
+ case 0x10: /* amomin.w */ \
+ case 0x14: /* amomax.w */ \
+ case 0x18: /* amominu.w */ \
+ case 0x1c: /* amomaxu.w */ \
+ if (target_read_u ## size(s, &rval, addr)) \
+ goto mmu_exception; \
+ val = (int## size ## _t)rval; \
+ val2 = s->reg[rs2]; \
+ switch(funct3) { \
+ case 1: /* amiswap.w */ \
+ break; \
+ case 0: /* amoadd.w */ \
+ val2 = (int## size ## _t)(val + val2); \
+ break; \
+ case 4: /* amoxor.w */ \
+ val2 = (int## size ## _t)(val ^ val2); \
+ break; \
+ case 0xc: /* amoand.w */ \
+ val2 = (int## size ## _t)(val & val2); \
+ break; \
+ case 0x8: /* amoor.w */ \
+ val2 = (int## size ## _t)(val | val2); \
+ break; \
+ case 0x10: /* amomin.w */ \
+ if ((int## size ## _t)val < (int## size ## _t)val2) \
+ val2 = (int## size ## _t)val; \
+ break; \
+ case 0x14: /* amomax.w */ \
+ if ((int## size ## _t)val > (int## size ## _t)val2) \
+ val2 = (int## size ## _t)val; \
+ break; \
+ case 0x18: /* amominu.w */ \
+ if ((uint## size ## _t)val < (uint## size ## _t)val2) \
+ val2 = (int## size ## _t)val; \
+ break; \
+ case 0x1c: /* amomaxu.w */ \
+ if ((uint## size ## _t)val > (uint## size ## _t)val2) \
+ val2 = (int## size ## _t)val; \
+ break; \
+ default: \
+ goto illegal_insn; \
+ } \
+ if (target_write_u ## size(s, addr, val2)) \
+ goto mmu_exception; \
+ break; \
+ default: \
+ goto illegal_insn; \
+ } \
+ }
+
+ switch(funct3) {
+ case 2:
+ OP_A(32);
+ break;
+#if XLEN >= 64
+ case 3:
+ OP_A(64);
+ break;
+#endif
+#if XLEN >= 128
+ case 4:
+ OP_A(128);
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ if (rd != 0)
+ s->reg[rd] = val;
+ NEXT_INSN;
+#if FLEN > 0
+ /* FPU */
+ case 0x07: /* fp load */
+ if (s->fs == 0)
+ goto illegal_insn;
+ funct3 = (insn >> 12) & 7;
+ imm = (int32_t)insn >> 20;
+ addr = s->reg[rs1] + imm;
+ switch(funct3) {
+ case 2: /* flw */
+ {
+ uint32_t rval;
+ if (target_read_u32(s, &rval, addr))
+ goto mmu_exception;
+ s->fp_reg[rd] = rval | F32_HIGH;
+ }
+ break;
+#if FLEN >= 64
+ case 3: /* fld */
+ {
+ uint64_t rval;
+ if (target_read_u64(s, &rval, addr))
+ goto mmu_exception;
+ s->fp_reg[rd] = rval | F64_HIGH;
+ }
+ break;
+#endif
+#if FLEN >= 128
+ case 4: /* flq */
+ {
+ uint128_t rval;
+ if (target_read_u128(s, &rval, addr))
+ goto mmu_exception;
+ s->fp_reg[rd] = rval;
+ }
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ NEXT_INSN;
+ case 0x27: /* fp store */
+ if (s->fs == 0)
+ goto illegal_insn;
+ funct3 = (insn >> 12) & 7;
+ imm = rd | ((insn >> (25 - 5)) & 0xfe0);
+ imm = (imm << 20) >> 20;
+ addr = s->reg[rs1] + imm;
+ switch(funct3) {
+ case 2: /* fsw */
+ if (target_write_u32(s, addr, s->fp_reg[rs2]))
+ goto mmu_exception;
+ break;
+#if FLEN >= 64
+ case 3: /* fsd */
+ if (target_write_u64(s, addr, s->fp_reg[rs2]))
+ goto mmu_exception;
+ break;
+#endif
+#if FLEN >= 128
+ case 4: /* fsq */
+ if (target_write_u128(s, addr, s->fp_reg[rs2]))
+ goto mmu_exception;
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ NEXT_INSN;
+ case 0x43: /* fmadd */
+ if (s->fs == 0)
+ goto illegal_insn;
+ funct3 = (insn >> 25) & 3;
+ rs3 = insn >> 27;
+ rm = get_insn_rm(s, (insn >> 12) & 7);
+ if (rm < 0)
+ goto illegal_insn;
+ switch(funct3) {
+ case 0:
+ s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1], s->fp_reg[rs2],
+ s->fp_reg[rs3], rm, &s->fflags) | F32_HIGH;
+ break;
+#if FLEN >= 64
+ case 1:
+ s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1], s->fp_reg[rs2],
+ s->fp_reg[rs3], rm, &s->fflags) | F64_HIGH;
+ break;
+#endif
+#if FLEN >= 128
+ case 3:
+ s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1], s->fp_reg[rs2],
+ s->fp_reg[rs3], rm, &s->fflags);
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ NEXT_INSN;
+ case 0x47: /* fmsub */
+ if (s->fs == 0)
+ goto illegal_insn;
+ funct3 = (insn >> 25) & 3;
+ rs3 = insn >> 27;
+ rm = get_insn_rm(s, (insn >> 12) & 7);
+ if (rm < 0)
+ goto illegal_insn;
+ switch(funct3) {
+ case 0:
+ s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ s->fp_reg[rs3] ^ FSIGN_MASK32,
+ rm, &s->fflags) | F32_HIGH;
+ break;
+#if FLEN >= 64
+ case 1:
+ s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ s->fp_reg[rs3] ^ FSIGN_MASK64,
+ rm, &s->fflags) | F64_HIGH;
+ break;
+#endif
+#if FLEN >= 128
+ case 3:
+ s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1],
+ s->fp_reg[rs2],
+ s->fp_reg[rs3] ^ FSIGN_MASK128,
+ rm, &s->fflags);
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ NEXT_INSN;
+ case 0x4b: /* fnmsub */
+ if (s->fs == 0)
+ goto illegal_insn;
+ funct3 = (insn >> 25) & 3;
+ rs3 = insn >> 27;
+ rm = get_insn_rm(s, (insn >> 12) & 7);
+ if (rm < 0)
+ goto illegal_insn;
+ switch(funct3) {
+ case 0:
+ s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1] ^ FSIGN_MASK32,
+ s->fp_reg[rs2],
+ s->fp_reg[rs3],
+ rm, &s->fflags) | F32_HIGH;
+ break;
+#if FLEN >= 64
+ case 1:
+ s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1] ^ FSIGN_MASK64,
+ s->fp_reg[rs2],
+ s->fp_reg[rs3],
+ rm, &s->fflags) | F64_HIGH;
+ break;
+#endif
+#if FLEN >= 128
+ case 3:
+ s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1] ^ FSIGN_MASK128,
+ s->fp_reg[rs2],
+ s->fp_reg[rs3],
+ rm, &s->fflags);
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ NEXT_INSN;
+ case 0x4f: /* fnmadd */
+ if (s->fs == 0)
+ goto illegal_insn;
+ funct3 = (insn >> 25) & 3;
+ rs3 = insn >> 27;
+ rm = get_insn_rm(s, (insn >> 12) & 7);
+ if (rm < 0)
+ goto illegal_insn;
+ switch(funct3) {
+ case 0:
+ s->fp_reg[rd] = fma_sf32(s->fp_reg[rs1] ^ FSIGN_MASK32,
+ s->fp_reg[rs2],
+ s->fp_reg[rs3] ^ FSIGN_MASK32,
+ rm, &s->fflags) | F32_HIGH;
+ break;
+#if FLEN >= 64
+ case 1:
+ s->fp_reg[rd] = fma_sf64(s->fp_reg[rs1] ^ FSIGN_MASK64,
+ s->fp_reg[rs2],
+ s->fp_reg[rs3] ^ FSIGN_MASK64,
+ rm, &s->fflags) | F64_HIGH;
+ break;
+#endif
+#if FLEN >= 128
+ case 3:
+ s->fp_reg[rd] = fma_sf128(s->fp_reg[rs1] ^ FSIGN_MASK128,
+ s->fp_reg[rs2],
+ s->fp_reg[rs3] ^ FSIGN_MASK128,
+ rm, &s->fflags);
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ s->fs = 3;
+ NEXT_INSN;
+ case 0x53:
+ if (s->fs == 0)
+ goto illegal_insn;
+ imm = insn >> 25;
+ rm = (insn >> 12) & 7;
+ switch(imm) {
+
+#define F_SIZE 32
+#include "riscv_cpu_fp_template.h"
+#if FLEN >= 64
+#define F_SIZE 64
+#include "riscv_cpu_fp_template.h"
+#endif
+#if FLEN >= 128
+#define F_SIZE 128
+#include "riscv_cpu_fp_template.h"
+#endif
+
+ default:
+ goto illegal_insn;
+ }
+ NEXT_INSN;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ /* update PC for next instruction */
+ jump_insn: ;
+ } /* end of main loop */
+ illegal_insn:
+ s->pending_exception = CAUSE_ILLEGAL_INSTRUCTION;
+ s->pending_tval = insn;
+ mmu_exception:
+ exception:
+ s->pc = GET_PC();
+ if (s->pending_exception >= 0) {
+ /* Note: the idea is that one exception counts for one cycle. */
+ s->n_cycles--;
+ raise_exception2(s, s->pending_exception, s->pending_tval);
+ }
+ /* we exit because XLEN may have changed */
+ done_interp:
+the_end:
+ s->insn_counter = GET_INSN_COUNTER();
+#if 0
+ printf("done interp %lx int=%x mstatus=%lx prv=%d\n",
+ (uint64_t)s->insn_counter, s->mip & s->mie, (uint64_t)s->mstatus,
+ s->priv);
+#endif
+}
+
+#undef uintx_t
+#undef intx_t
+#undef XLEN
+#undef OP_A
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c
new file mode 100644
index 0000000..a7149fd
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c
@@ -0,0 +1,1053 @@
+/*
+ * RISCV machine
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "riscv_cpu.h"
+#include "virtio.h"
+#include "machine.h"
+
+/* RISCV machine */
+
+typedef struct RISCVMachine {
+ VirtMachine common;
+ PhysMemoryMap *mem_map;
+ int max_xlen;
+ RISCVCPUState *cpu_state;
+ uint64_t ram_size;
+ /* RTC */
+ BOOL rtc_real_time;
+ uint64_t rtc_start_time;
+ uint64_t timecmp;
+ /* PLIC */
+ uint32_t plic_pending_irq, plic_served_irq;
+ IRQSignal plic_irq[32]; /* IRQ 0 is not used */
+ /* HTIF */
+ uint64_t htif_tohost, htif_fromhost;
+
+ VIRTIODevice *keyboard_dev;
+ VIRTIODevice *mouse_dev;
+
+ int virtio_count;
+} RISCVMachine;
+
+#define LOW_RAM_SIZE 0x00010000 /* 64KB */
+#define RAM_BASE_ADDR 0x80000000
+#define CLINT_BASE_ADDR 0x02000000
+#define CLINT_SIZE 0x000c0000
+#define HTIF_BASE_ADDR 0x40008000
+#define IDE_BASE_ADDR 0x40009000
+#define VIRTIO_BASE_ADDR 0x40010000
+#define VIRTIO_SIZE 0x1000
+#define VIRTIO_IRQ 1
+#define PLIC_BASE_ADDR 0x40100000
+#define PLIC_SIZE 0x00400000
+#define FRAMEBUFFER_BASE_ADDR 0x41000000
+
+#define RTC_FREQ 10000000
+#define RTC_FREQ_DIV 16 /* arbitrary, relative to CPU freq to have a
+ 10 MHz frequency */
+
+static uint64_t rtc_get_real_time(RISCVMachine *s)
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint64_t)ts.tv_sec * RTC_FREQ +
+ (ts.tv_nsec / (1000000000 / RTC_FREQ));
+}
+
+static uint64_t rtc_get_time(RISCVMachine *m)
+{
+ uint64_t val;
+ if (m->rtc_real_time) {
+ val = rtc_get_real_time(m) - m->rtc_start_time;
+ } else {
+ val = riscv_cpu_get_cycles(m->cpu_state) / RTC_FREQ_DIV;
+ }
+ // printf("rtc_time=%" PRId64 "\n", val);
+ return val;
+}
+
+static uint32_t htif_read(void *opaque, uint32_t offset,
+ int size_log2)
+{
+ RISCVMachine *s = opaque;
+ uint32_t val;
+
+ assert(size_log2 == 2);
+ switch(offset) {
+ case 0:
+ val = s->htif_tohost;
+ break;
+ case 4:
+ val = s->htif_tohost >> 32;
+ break;
+ case 8:
+ val = s->htif_fromhost;
+ break;
+ case 12:
+ val = s->htif_fromhost >> 32;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static void htif_handle_cmd(RISCVMachine *s)
+{
+ uint32_t device, cmd;
+
+ device = s->htif_tohost >> 56;
+ cmd = (s->htif_tohost >> 48) & 0xff;
+ if (s->htif_tohost == 1) {
+ /* shuthost */
+ printf("\nPower off.\n");
+ exit(0);
+ } else if (device == 1 && cmd == 1) {
+ uint8_t buf[1];
+ buf[0] = s->htif_tohost & 0xff;
+ s->common.console->write_data(s->common.console->opaque, buf, 1);
+ s->htif_tohost = 0;
+ s->htif_fromhost = ((uint64_t)device << 56) | ((uint64_t)cmd << 48);
+ } else if (device == 1 && cmd == 0) {
+ /* request keyboard interrupt */
+ s->htif_tohost = 0;
+ } else {
+ printf("HTIF: unsupported tohost=0x%016" PRIx64 "\n", s->htif_tohost);
+ }
+}
+
+static void htif_write(void *opaque, uint32_t offset, uint32_t val,
+ int size_log2)
+{
+ RISCVMachine *s = opaque;
+
+ assert(size_log2 == 2);
+ switch(offset) {
+ case 0:
+ s->htif_tohost = (s->htif_tohost & ~0xffffffff) | val;
+ break;
+ case 4:
+ s->htif_tohost = (s->htif_tohost & 0xffffffff) | ((uint64_t)val << 32);
+ htif_handle_cmd(s);
+ break;
+ case 8:
+ s->htif_fromhost = (s->htif_fromhost & ~0xffffffff) | val;
+ break;
+ case 12:
+ s->htif_fromhost = (s->htif_fromhost & 0xffffffff) |
+ (uint64_t)val << 32;
+ break;
+ default:
+ break;
+ }
+}
+
+#if 0
+static void htif_poll(RISCVMachine *s)
+{
+ uint8_t buf[1];
+ int ret;
+
+ if (s->htif_fromhost == 0) {
+ ret = s->console->read_data(s->console->opaque, buf, 1);
+ if (ret == 1) {
+ s->htif_fromhost = ((uint64_t)1 << 56) | ((uint64_t)0 << 48) |
+ buf[0];
+ }
+ }
+}
+#endif
+
+static uint32_t clint_read(void *opaque, uint32_t offset, int size_log2)
+{
+ RISCVMachine *m = opaque;
+ uint32_t val;
+
+ assert(size_log2 == 2);
+ switch(offset) {
+ case 0xbff8:
+ val = rtc_get_time(m);
+ break;
+ case 0xbffc:
+ val = rtc_get_time(m) >> 32;
+ break;
+ case 0x4000:
+ val = m->timecmp;
+ break;
+ case 0x4004:
+ val = m->timecmp >> 32;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static void clint_write(void *opaque, uint32_t offset, uint32_t val,
+ int size_log2)
+{
+ RISCVMachine *m = opaque;
+
+ assert(size_log2 == 2);
+ switch(offset) {
+ case 0x4000:
+ m->timecmp = (m->timecmp & ~0xffffffff) | val;
+ riscv_cpu_reset_mip(m->cpu_state, MIP_MTIP);
+ break;
+ case 0x4004:
+ m->timecmp = (m->timecmp & 0xffffffff) | ((uint64_t)val << 32);
+ riscv_cpu_reset_mip(m->cpu_state, MIP_MTIP);
+ break;
+ default:
+ break;
+ }
+}
+
+static void plic_update_mip(RISCVMachine *s)
+{
+ RISCVCPUState *cpu = s->cpu_state;
+ uint32_t mask;
+ mask = s->plic_pending_irq & ~s->plic_served_irq;
+ if (mask) {
+ riscv_cpu_set_mip(cpu, MIP_MEIP | MIP_SEIP);
+ } else {
+ riscv_cpu_reset_mip(cpu, MIP_MEIP | MIP_SEIP);
+ }
+}
+
+#define PLIC_HART_BASE 0x200000
+#define PLIC_HART_SIZE 0x1000
+
+static uint32_t plic_read(void *opaque, uint32_t offset, int size_log2)
+{
+ RISCVMachine *s = opaque;
+ uint32_t val, mask;
+ int i;
+ assert(size_log2 == 2);
+ switch(offset) {
+ case PLIC_HART_BASE:
+ val = 0;
+ break;
+ case PLIC_HART_BASE + 4:
+ mask = s->plic_pending_irq & ~s->plic_served_irq;
+ if (mask != 0) {
+ i = ctz32(mask);
+ s->plic_served_irq |= 1 << i;
+ plic_update_mip(s);
+ val = i + 1;
+ } else {
+ val = 0;
+ }
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static void plic_write(void *opaque, uint32_t offset, uint32_t val,
+ int size_log2)
+{
+ RISCVMachine *s = opaque;
+
+ assert(size_log2 == 2);
+ switch(offset) {
+ case PLIC_HART_BASE + 4:
+ val--;
+ if (val < 32) {
+ s->plic_served_irq &= ~(1 << val);
+ plic_update_mip(s);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void plic_set_irq(void *opaque, int irq_num, int state)
+{
+ RISCVMachine *s = opaque;
+ uint32_t mask;
+
+ mask = 1 << (irq_num - 1);
+ if (state)
+ s->plic_pending_irq |= mask;
+ else
+ s->plic_pending_irq &= ~mask;
+ plic_update_mip(s);
+}
+
+static uint8_t *get_ram_ptr(RISCVMachine *s, uint64_t paddr, BOOL is_rw)
+{
+ return phys_mem_get_ram_ptr(s->mem_map, paddr, is_rw);
+}
+
+/* FDT machine description */
+
+#define FDT_MAGIC 0xd00dfeed
+#define FDT_VERSION 17
+
+struct fdt_header {
+ uint32_t magic;
+ uint32_t totalsize;
+ uint32_t off_dt_struct;
+ uint32_t off_dt_strings;
+ uint32_t off_mem_rsvmap;
+ uint32_t version;
+ uint32_t last_comp_version; /* <= 17 */
+ uint32_t boot_cpuid_phys;
+ uint32_t size_dt_strings;
+ uint32_t size_dt_struct;
+};
+
+struct fdt_reserve_entry {
+ uint64_t address;
+ uint64_t size;
+};
+
+#define FDT_BEGIN_NODE 1
+#define FDT_END_NODE 2
+#define FDT_PROP 3
+#define FDT_NOP 4
+#define FDT_END 9
+
+typedef struct {
+ uint32_t *tab;
+ int tab_len;
+ int tab_size;
+ int open_node_count;
+
+ char *string_table;
+ int string_table_len;
+ int string_table_size;
+} FDTState;
+
+static FDTState *fdt_init(void)
+{
+ FDTState *s;
+ s = mallocz(sizeof(*s));
+ return s;
+}
+
+static void fdt_alloc_len(FDTState *s, int len)
+{
+ int new_size;
+ if (unlikely(len > s->tab_size)) {
+ new_size = max_int(len, s->tab_size * 3 / 2);
+ s->tab = realloc(s->tab, new_size * sizeof(uint32_t));
+ s->tab_size = new_size;
+ }
+}
+
+static void fdt_put32(FDTState *s, int v)
+{
+ fdt_alloc_len(s, s->tab_len + 1);
+ s->tab[s->tab_len++] = cpu_to_be32(v);
+}
+
+/* the data is zero padded */
+static void fdt_put_data(FDTState *s, const uint8_t *data, int len)
+{
+ int len1;
+
+ len1 = (len + 3) / 4;
+ fdt_alloc_len(s, s->tab_len + len1);
+ memcpy(s->tab + s->tab_len, data, len);
+ memset((uint8_t *)(s->tab + s->tab_len) + len, 0, -len & 3);
+ s->tab_len += len1;
+}
+
+static void fdt_begin_node(FDTState *s, const char *name)
+{
+ fdt_put32(s, FDT_BEGIN_NODE);
+ fdt_put_data(s, (uint8_t *)name, strlen(name) + 1);
+ s->open_node_count++;
+}
+
+static void fdt_begin_node_num(FDTState *s, const char *name, uint64_t n)
+{
+ char buf[256];
+ snprintf(buf, sizeof(buf), "%s@%" PRIx64, name, n);
+ fdt_begin_node(s, buf);
+}
+
+static void fdt_end_node(FDTState *s)
+{
+ fdt_put32(s, FDT_END_NODE);
+ s->open_node_count--;
+}
+
+static int fdt_get_string_offset(FDTState *s, const char *name)
+{
+ int pos, new_size, name_size, new_len;
+
+ pos = 0;
+ while (pos < s->string_table_len) {
+ if (!strcmp(s->string_table + pos, name))
+ return pos;
+ pos += strlen(s->string_table + pos) + 1;
+ }
+ /* add a new string */
+ name_size = strlen(name) + 1;
+ new_len = s->string_table_len + name_size;
+ if (new_len > s->string_table_size) {
+ new_size = max_int(new_len, s->string_table_size * 3 / 2);
+ s->string_table = realloc(s->string_table, new_size);
+ s->string_table_size = new_size;
+ }
+ pos = s->string_table_len;
+ memcpy(s->string_table + pos, name, name_size);
+ s->string_table_len = new_len;
+ return pos;
+}
+
+static void fdt_prop(FDTState *s, const char *prop_name,
+ const void *data, int data_len)
+{
+ fdt_put32(s, FDT_PROP);
+ fdt_put32(s, data_len);
+ fdt_put32(s, fdt_get_string_offset(s, prop_name));
+ fdt_put_data(s, data, data_len);
+}
+
+static void fdt_prop_tab_u32(FDTState *s, const char *prop_name,
+ uint32_t *tab, int tab_len)
+{
+ int i;
+ fdt_put32(s, FDT_PROP);
+ fdt_put32(s, tab_len * sizeof(uint32_t));
+ fdt_put32(s, fdt_get_string_offset(s, prop_name));
+ for(i = 0; i < tab_len; i++)
+ fdt_put32(s, tab[i]);
+}
+
+static void fdt_prop_u32(FDTState *s, const char *prop_name, uint32_t val)
+{
+ fdt_prop_tab_u32(s, prop_name, &val, 1);
+}
+
+static void fdt_prop_tab_u64(FDTState *s, const char *prop_name,
+ uint64_t v0)
+{
+ uint32_t tab[2];
+ tab[0] = v0 >> 32;
+ tab[1] = v0;
+ fdt_prop_tab_u32(s, prop_name, tab, 2);
+}
+
+static void fdt_prop_tab_u64_2(FDTState *s, const char *prop_name,
+ uint64_t v0, uint64_t v1)
+{
+ uint32_t tab[4];
+ tab[0] = v0 >> 32;
+ tab[1] = v0;
+ tab[2] = v1 >> 32;
+ tab[3] = v1;
+ fdt_prop_tab_u32(s, prop_name, tab, 4);
+}
+
+static void fdt_prop_str(FDTState *s, const char *prop_name,
+ const char *str)
+{
+ fdt_prop(s, prop_name, str, strlen(str) + 1);
+}
+
+/* NULL terminated string list */
+static void fdt_prop_tab_str(FDTState *s, const char *prop_name,
+ ...)
+{
+ va_list ap;
+ int size, str_size;
+ char *ptr, *tab;
+
+ va_start(ap, prop_name);
+ size = 0;
+ for(;;) {
+ ptr = va_arg(ap, char *);
+ if (!ptr)
+ break;
+ str_size = strlen(ptr) + 1;
+ size += str_size;
+ }
+ va_end(ap);
+
+ tab = malloc(size);
+ va_start(ap, prop_name);
+ size = 0;
+ for(;;) {
+ ptr = va_arg(ap, char *);
+ if (!ptr)
+ break;
+ str_size = strlen(ptr) + 1;
+ memcpy(tab + size, ptr, str_size);
+ size += str_size;
+ }
+ va_end(ap);
+
+ fdt_prop(s, prop_name, tab, size);
+ free(tab);
+}
+
+/* write the FDT to 'dst1'. return the FDT size in bytes */
+int fdt_output(FDTState *s, uint8_t *dst)
+{
+ struct fdt_header *h;
+ struct fdt_reserve_entry *re;
+ int dt_struct_size;
+ int dt_strings_size;
+ int pos;
+
+ assert(s->open_node_count == 0);
+
+ fdt_put32(s, FDT_END);
+
+ dt_struct_size = s->tab_len * sizeof(uint32_t);
+ dt_strings_size = s->string_table_len;
+
+ h = (struct fdt_header *)dst;
+ h->magic = cpu_to_be32(FDT_MAGIC);
+ h->version = cpu_to_be32(FDT_VERSION);
+ h->last_comp_version = cpu_to_be32(16);
+ h->boot_cpuid_phys = cpu_to_be32(0);
+ h->size_dt_strings = cpu_to_be32(dt_strings_size);
+ h->size_dt_struct = cpu_to_be32(dt_struct_size);
+
+ pos = sizeof(struct fdt_header);
+
+ h->off_dt_struct = cpu_to_be32(pos);
+ memcpy(dst + pos, s->tab, dt_struct_size);
+ pos += dt_struct_size;
+
+ /* align to 8 */
+ while ((pos & 7) != 0) {
+ dst[pos++] = 0;
+ }
+ h->off_mem_rsvmap = cpu_to_be32(pos);
+ re = (struct fdt_reserve_entry *)(dst + pos);
+ re->address = 0; /* no reserved entry */
+ re->size = 0;
+ pos += sizeof(struct fdt_reserve_entry);
+
+ h->off_dt_strings = cpu_to_be32(pos);
+ memcpy(dst + pos, s->string_table, dt_strings_size);
+ pos += dt_strings_size;
+
+ /* align to 8, just in case */
+ while ((pos & 7) != 0) {
+ dst[pos++] = 0;
+ }
+
+ h->totalsize = cpu_to_be32(pos);
+ return pos;
+}
+
+void fdt_end(FDTState *s)
+{
+ free(s->tab);
+ free(s->string_table);
+ free(s);
+}
+
+static int riscv_build_fdt(RISCVMachine *m, uint8_t *dst,
+ uint64_t kernel_start, uint64_t kernel_size,
+ uint64_t initrd_start, uint64_t initrd_size,
+ const char *cmd_line)
+{
+ FDTState *s;
+ int size, max_xlen, i, cur_phandle, intc_phandle, plic_phandle;
+ char isa_string[128], *q;
+ uint32_t misa;
+ uint32_t tab[4];
+ FBDevice *fb_dev;
+
+ s = fdt_init();
+
+ cur_phandle = 1;
+
+ fdt_begin_node(s, "");
+ fdt_prop_u32(s, "#address-cells", 2);
+ fdt_prop_u32(s, "#size-cells", 2);
+ fdt_prop_str(s, "compatible", "ucbbar,riscvemu-bar_dev");
+ fdt_prop_str(s, "model", "ucbbar,riscvemu-bare");
+
+ /* CPU list */
+ fdt_begin_node(s, "cpus");
+ fdt_prop_u32(s, "#address-cells", 1);
+ fdt_prop_u32(s, "#size-cells", 0);
+ fdt_prop_u32(s, "timebase-frequency", RTC_FREQ);
+
+ /* cpu */
+ fdt_begin_node_num(s, "cpu", 0);
+ fdt_prop_str(s, "device_type", "cpu");
+ fdt_prop_u32(s, "reg", 0);
+ fdt_prop_str(s, "status", "okay");
+ fdt_prop_str(s, "compatible", "riscv");
+
+ max_xlen = m->max_xlen;
+ misa = riscv_cpu_get_misa(m->cpu_state);
+ q = isa_string;
+ q += snprintf(isa_string, sizeof(isa_string), "rv%d", max_xlen);
+ for(i = 0; i < 26; i++) {
+ if (misa & (1 << i))
+ *q++ = 'a' + i;
+ }
+ *q = '\0';
+ fdt_prop_str(s, "riscv,isa", isa_string);
+
+ fdt_prop_str(s, "mmu-type", max_xlen <= 32 ? "riscv,sv32" : "riscv,sv48");
+ fdt_prop_u32(s, "clock-frequency", 2000000000);
+
+ fdt_begin_node(s, "interrupt-controller");
+ fdt_prop_u32(s, "#interrupt-cells", 1);
+ fdt_prop(s, "interrupt-controller", NULL, 0);
+ fdt_prop_str(s, "compatible", "riscv,cpu-intc");
+ intc_phandle = cur_phandle++;
+ fdt_prop_u32(s, "phandle", intc_phandle);
+ fdt_end_node(s); /* interrupt-controller */
+
+ fdt_end_node(s); /* cpu */
+
+ fdt_end_node(s); /* cpus */
+
+ fdt_begin_node_num(s, "memory", RAM_BASE_ADDR);
+ fdt_prop_str(s, "device_type", "memory");
+ tab[0] = (uint64_t)RAM_BASE_ADDR >> 32;
+ tab[1] = RAM_BASE_ADDR;
+ tab[2] = m->ram_size >> 32;
+ tab[3] = m->ram_size;
+ fdt_prop_tab_u32(s, "reg", tab, 4);
+
+ fdt_end_node(s); /* memory */
+
+ fdt_begin_node(s, "htif");
+ fdt_prop_str(s, "compatible", "ucb,htif0");
+ fdt_end_node(s); /* htif */
+
+ fdt_begin_node(s, "soc");
+ fdt_prop_u32(s, "#address-cells", 2);
+ fdt_prop_u32(s, "#size-cells", 2);
+ fdt_prop_tab_str(s, "compatible",
+ "ucbbar,riscvemu-bar-soc", "simple-bus", NULL);
+ fdt_prop(s, "ranges", NULL, 0);
+
+ fdt_begin_node_num(s, "clint", CLINT_BASE_ADDR);
+ fdt_prop_str(s, "compatible", "riscv,clint0");
+
+ tab[0] = intc_phandle;
+ tab[1] = 3; /* M IPI irq */
+ tab[2] = intc_phandle;
+ tab[3] = 7; /* M timer irq */
+ fdt_prop_tab_u32(s, "interrupts-extended", tab, 4);
+
+ fdt_prop_tab_u64_2(s, "reg", CLINT_BASE_ADDR, CLINT_SIZE);
+
+ fdt_end_node(s); /* clint */
+
+ fdt_begin_node_num(s, "plic", PLIC_BASE_ADDR);
+ fdt_prop_u32(s, "#interrupt-cells", 1);
+ fdt_prop(s, "interrupt-controller", NULL, 0);
+ fdt_prop_str(s, "compatible", "riscv,plic0");
+ fdt_prop_u32(s, "riscv,ndev", 31);
+ fdt_prop_tab_u64_2(s, "reg", PLIC_BASE_ADDR, PLIC_SIZE);
+
+ tab[0] = intc_phandle;
+ tab[1] = 9; /* S ext irq */
+ tab[2] = intc_phandle;
+ tab[3] = 11; /* M ext irq */
+ fdt_prop_tab_u32(s, "interrupts-extended", tab, 4);
+
+ plic_phandle = cur_phandle++;
+ fdt_prop_u32(s, "phandle", plic_phandle);
+
+ fdt_end_node(s); /* plic */
+
+ for(i = 0; i < m->virtio_count; i++) {
+ fdt_begin_node_num(s, "virtio", VIRTIO_BASE_ADDR + i * VIRTIO_SIZE);
+ fdt_prop_str(s, "compatible", "virtio,mmio");
+ fdt_prop_tab_u64_2(s, "reg", VIRTIO_BASE_ADDR + i * VIRTIO_SIZE,
+ VIRTIO_SIZE);
+ tab[0] = plic_phandle;
+ tab[1] = VIRTIO_IRQ + i;
+ fdt_prop_tab_u32(s, "interrupts-extended", tab, 2);
+ fdt_end_node(s); /* virtio */
+ }
+
+ fb_dev = m->common.fb_dev;
+ if (fb_dev) {
+ fdt_begin_node_num(s, "framebuffer", FRAMEBUFFER_BASE_ADDR);
+ fdt_prop_str(s, "compatible", "simple-framebuffer");
+ fdt_prop_tab_u64_2(s, "reg", FRAMEBUFFER_BASE_ADDR, fb_dev->fb_size);
+ fdt_prop_u32(s, "width", fb_dev->width);
+ fdt_prop_u32(s, "height", fb_dev->height);
+ fdt_prop_u32(s, "stride", fb_dev->stride);
+ fdt_prop_str(s, "format", "a8r8g8b8");
+ fdt_end_node(s); /* framebuffer */
+ }
+
+ fdt_end_node(s); /* soc */
+
+ fdt_begin_node(s, "chosen");
+ fdt_prop_str(s, "bootargs", cmd_line ? cmd_line : "");
+ if (kernel_size > 0) {
+ fdt_prop_tab_u64(s, "riscv,kernel-start", kernel_start);
+ fdt_prop_tab_u64(s, "riscv,kernel-end", kernel_start + kernel_size);
+ }
+ if (initrd_size > 0) {
+ fdt_prop_tab_u64(s, "linux,initrd-start", initrd_start);
+ fdt_prop_tab_u64(s, "linux,initrd-end", initrd_start + initrd_size);
+ }
+
+
+ fdt_end_node(s); /* chosen */
+
+ fdt_end_node(s); /* / */
+
+ size = fdt_output(s, dst);
+#if 0
+ {
+ FILE *f;
+ f = fopen("/tmp/riscvemu.dtb", "wb");
+ fwrite(dst, 1, size, f);
+ fclose(f);
+ }
+#endif
+ fdt_end(s);
+ return size;
+}
+
+static void copy_bios(RISCVMachine *s, const uint8_t *buf, int buf_len,
+ const uint8_t *kernel_buf, int kernel_buf_len,
+ const uint8_t *initrd_buf, int initrd_buf_len,
+ const char *cmd_line)
+{
+ uint32_t fdt_addr, align, kernel_base, initrd_base;
+ uint8_t *ram_ptr;
+ uint32_t *q;
+
+ if (buf_len > s->ram_size) {
+ vm_error("BIOS too big\n");
+ exit(1);
+ }
+
+ ram_ptr = get_ram_ptr(s, RAM_BASE_ADDR, TRUE);
+ memcpy(ram_ptr, buf, buf_len);
+
+ kernel_base = 0;
+ if (kernel_buf_len > 0) {
+ /* copy the kernel if present */
+ if (s->max_xlen == 32)
+ align = 4 << 20; /* 4 MB page align */
+ else
+ align = 2 << 20; /* 2 MB page align */
+ kernel_base = (buf_len + align - 1) & ~(align - 1);
+ memcpy(ram_ptr + kernel_base, kernel_buf, kernel_buf_len);
+ if (kernel_buf_len + kernel_base > s->ram_size) {
+ vm_error("kernel too big");
+ exit(1);
+ }
+ }
+
+ initrd_base = 0;
+ if (initrd_buf_len > 0) {
+ /* same allocation as QEMU */
+ initrd_base = s->ram_size / 2;
+ if (initrd_base > (128 << 20))
+ initrd_base = 128 << 20;
+ memcpy(ram_ptr + initrd_base, initrd_buf, initrd_buf_len);
+ if (initrd_buf_len + initrd_base > s->ram_size) {
+ vm_error("initrd too big");
+ exit(1);
+ }
+ }
+
+ ram_ptr = get_ram_ptr(s, 0, TRUE);
+
+ fdt_addr = 0x1000 + 8 * 8;
+
+ riscv_build_fdt(s, ram_ptr + fdt_addr,
+ RAM_BASE_ADDR + kernel_base, kernel_buf_len,
+ RAM_BASE_ADDR + initrd_base, initrd_buf_len,
+ cmd_line);
+
+ /* jump_addr = 0x80000000 */
+
+ q = (uint32_t *)(ram_ptr + 0x1000);
+ q[0] = 0x297 + 0x80000000 - 0x1000; /* auipc t0, jump_addr */
+ q[1] = 0x597; /* auipc a1, dtb */
+ q[2] = 0x58593 + ((fdt_addr - 4) << 20); /* addi a1, a1, dtb */
+ q[3] = 0xf1402573; /* csrr a0, mhartid */
+ q[4] = 0x00028067; /* jalr zero, t0, jump_addr */
+}
+
+static void riscv_flush_tlb_write_range(void *opaque, uint8_t *ram_addr,
+ size_t ram_size)
+{
+ RISCVMachine *s = opaque;
+ riscv_cpu_flush_tlb_write_range_ram(s->cpu_state, ram_addr, ram_size);
+}
+
+static void riscv_machine_set_defaults(VirtMachineParams *p)
+{
+}
+
+static VirtMachine *riscv_machine_init(const VirtMachineParams *p)
+{
+ RISCVMachine *s;
+ VIRTIODevice *blk_dev;
+ int irq_num, i, max_xlen, ram_flags;
+ VIRTIOBusDef vbus_s, *vbus = &vbus_s;
+
+
+ if (!strcmp(p->machine_name, "riscv32")) {
+ max_xlen = 32;
+ } else if (!strcmp(p->machine_name, "riscv64")) {
+ max_xlen = 64;
+ } else if (!strcmp(p->machine_name, "riscv128")) {
+ max_xlen = 128;
+ } else {
+ vm_error("unsupported machine: %s\n", p->machine_name);
+ return NULL;
+ }
+
+ s = mallocz(sizeof(*s));
+ s->common.vmc = p->vmc;
+ s->ram_size = p->ram_size;
+ s->max_xlen = max_xlen;
+ s->mem_map = phys_mem_map_init();
+ /* needed to handle the RAM dirty bits */
+ s->mem_map->opaque = s;
+ s->mem_map->flush_tlb_write_range = riscv_flush_tlb_write_range;
+
+ s->cpu_state = riscv_cpu_init(s->mem_map, max_xlen);
+ if (!s->cpu_state) {
+ vm_error("unsupported max_xlen=%d\n", max_xlen);
+ /* XXX: should free resources */
+ return NULL;
+ }
+ /* RAM */
+ ram_flags = 0;
+ cpu_register_ram(s->mem_map, RAM_BASE_ADDR, p->ram_size, ram_flags);
+ cpu_register_ram(s->mem_map, 0x00000000, LOW_RAM_SIZE, 0);
+ s->rtc_real_time = p->rtc_real_time;
+ if (p->rtc_real_time) {
+ s->rtc_start_time = rtc_get_real_time(s);
+ }
+
+ cpu_register_device(s->mem_map, CLINT_BASE_ADDR, CLINT_SIZE, s,
+ clint_read, clint_write, DEVIO_SIZE32);
+ cpu_register_device(s->mem_map, PLIC_BASE_ADDR, PLIC_SIZE, s,
+ plic_read, plic_write, DEVIO_SIZE32);
+ for(i = 1; i < 32; i++) {
+ irq_init(&s->plic_irq[i], plic_set_irq, s, i);
+ }
+
+ cpu_register_device(s->mem_map, HTIF_BASE_ADDR, 16,
+ s, htif_read, htif_write, DEVIO_SIZE32);
+ s->common.console = p->console;
+
+ memset(vbus, 0, sizeof(*vbus));
+ vbus->mem_map = s->mem_map;
+ vbus->addr = VIRTIO_BASE_ADDR;
+ irq_num = VIRTIO_IRQ;
+
+ /* virtio console */
+ if (p->console) {
+ vbus->irq = &s->plic_irq[irq_num];
+ s->common.console_dev = virtio_console_init(vbus, p->console);
+ vbus->addr += VIRTIO_SIZE;
+ irq_num++;
+ s->virtio_count++;
+ }
+
+ /* virtio net device */
+ for(i = 0; i < p->eth_count; i++) {
+ vbus->irq = &s->plic_irq[irq_num];
+ virtio_net_init(vbus, p->tab_eth[i].net);
+ s->common.net = p->tab_eth[i].net;
+ vbus->addr += VIRTIO_SIZE;
+ irq_num++;
+ s->virtio_count++;
+ }
+
+ /* virtio block device */
+ for(i = 0; i < p->drive_count; i++) {
+ vbus->irq = &s->plic_irq[irq_num];
+ blk_dev = virtio_block_init(vbus, p->tab_drive[i].block_dev);
+ (void)blk_dev;
+ vbus->addr += VIRTIO_SIZE;
+ irq_num++;
+ s->virtio_count++;
+ }
+
+ /* virtio filesystem */
+ for(i = 0; i < p->fs_count; i++) {
+ VIRTIODevice *fs_dev;
+ vbus->irq = &s->plic_irq[irq_num];
+ fs_dev = virtio_9p_init(vbus, p->tab_fs[i].fs_dev,
+ p->tab_fs[i].tag);
+ (void)fs_dev;
+ // virtio_set_debug(fs_dev, VIRTIO_DEBUG_9P);
+ vbus->addr += VIRTIO_SIZE;
+ irq_num++;
+ s->virtio_count++;
+ }
+
+ if (p->display_device) {
+ FBDevice *fb_dev;
+ fb_dev = mallocz(sizeof(*fb_dev));
+ s->common.fb_dev = fb_dev;
+ if (!strcmp(p->display_device, "simplefb")) {
+ simplefb_init(s->mem_map,
+ FRAMEBUFFER_BASE_ADDR,
+ fb_dev,
+ p->width, p->height);
+
+ } else {
+ vm_error("unsupported display device: %s\n", p->display_device);
+ exit(1);
+ }
+ }
+
+ if (p->input_device) {
+ if (!strcmp(p->input_device, "virtio")) {
+ vbus->irq = &s->plic_irq[irq_num];
+ s->keyboard_dev = virtio_input_init(vbus,
+ VIRTIO_INPUT_TYPE_KEYBOARD);
+ vbus->addr += VIRTIO_SIZE;
+ irq_num++;
+ s->virtio_count++;
+
+ vbus->irq = &s->plic_irq[irq_num];
+ s->mouse_dev = virtio_input_init(vbus,
+ VIRTIO_INPUT_TYPE_TABLET);
+ vbus->addr += VIRTIO_SIZE;
+ irq_num++;
+ s->virtio_count++;
+ } else {
+ vm_error("unsupported input device: %s\n", p->input_device);
+ exit(1);
+ }
+ }
+
+ if (!p->files[VM_FILE_BIOS].buf) {
+ vm_error("No bios found");
+ }
+
+ copy_bios(s, p->files[VM_FILE_BIOS].buf, p->files[VM_FILE_BIOS].len,
+ p->files[VM_FILE_KERNEL].buf, p->files[VM_FILE_KERNEL].len,
+ p->files[VM_FILE_INITRD].buf, p->files[VM_FILE_INITRD].len,
+ p->cmdline);
+
+ return (VirtMachine *)s;
+}
+
+static void riscv_machine_end(VirtMachine *s1)
+{
+ RISCVMachine *s = (RISCVMachine *)s1;
+ /* XXX: stop all */
+ riscv_cpu_end(s->cpu_state);
+ phys_mem_map_end(s->mem_map);
+ free(s);
+}
+
+/* in ms */
+static int riscv_machine_get_sleep_duration(VirtMachine *s1, int delay)
+{
+ RISCVMachine *m = (RISCVMachine *)s1;
+ RISCVCPUState *s = m->cpu_state;
+ int64_t delay1;
+
+ /* wait for an event: the only asynchronous event is the RTC timer */
+ if (!(riscv_cpu_get_mip(s) & MIP_MTIP)) {
+ delay1 = m->timecmp - rtc_get_time(m);
+ if (delay1 <= 0) {
+ riscv_cpu_set_mip(s, MIP_MTIP);
+ delay = 0;
+ } else {
+ /* convert delay to ms */
+ delay1 = delay1 / (RTC_FREQ / 1000);
+ if (delay1 < delay)
+ delay = delay1;
+ }
+ }
+ if (!riscv_cpu_get_power_down(s))
+ delay = 0;
+ return delay;
+}
+
+static void riscv_machine_interp(VirtMachine *s1, int max_exec_cycle)
+{
+ RISCVMachine *s = (RISCVMachine *)s1;
+ riscv_cpu_interp(s->cpu_state, max_exec_cycle);
+}
+
+static void riscv_vm_send_key_event(VirtMachine *s1, BOOL is_down,
+ uint16_t key_code)
+{
+ RISCVMachine *s = (RISCVMachine *)s1;
+ if (s->keyboard_dev) {
+ virtio_input_send_key_event(s->keyboard_dev, is_down, key_code);
+ }
+}
+
+static BOOL riscv_vm_mouse_is_absolute(VirtMachine *s)
+{
+ return TRUE;
+}
+
+static void riscv_vm_send_mouse_event(VirtMachine *s1, int dx, int dy, int dz,
+ unsigned int buttons)
+{
+ RISCVMachine *s = (RISCVMachine *)s1;
+ if (s->mouse_dev) {
+ virtio_input_send_mouse_event(s->mouse_dev, dx, dy, dz, buttons);
+ }
+}
+
+const VirtMachineClass riscv_machine_class = {
+ "riscv32,riscv64,riscv128",
+ riscv_machine_set_defaults,
+ riscv_machine_init,
+ riscv_machine_end,
+ riscv_machine_get_sleep_duration,
+ riscv_machine_interp,
+ riscv_vm_mouse_is_absolute,
+ riscv_vm_send_mouse_event,
+ riscv_vm_send_key_event,
+};
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/sdl.c b/jslinux-2019-12-21/tinyemu-2019-12-21/sdl.c
new file mode 100644
index 0000000..c2afeba
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/sdl.c
@@ -0,0 +1,275 @@
+/*
+ * SDL display driver
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <SDL/SDL.h>
+
+#include "cutils.h"
+#include "virtio.h"
+#include "machine.h"
+
+#define KEYCODE_MAX 127
+
+static SDL_Surface *screen;
+static SDL_Surface *fb_surface;
+static int screen_width, screen_height, fb_width, fb_height, fb_stride;
+static SDL_Cursor *sdl_cursor_hidden;
+static uint8_t key_pressed[KEYCODE_MAX + 1];
+
+static void sdl_update_fb_surface(FBDevice *fb_dev)
+{
+ if (!fb_surface)
+ goto force_alloc;
+ if (fb_width != fb_dev->width ||
+ fb_height != fb_dev->height ||
+ fb_stride != fb_dev->stride) {
+ force_alloc:
+ if (fb_surface != NULL)
+ SDL_FreeSurface(fb_surface);
+ fb_width = fb_dev->width;
+ fb_height = fb_dev->height;
+ fb_stride = fb_dev->stride;
+ fb_surface = SDL_CreateRGBSurfaceFrom(fb_dev->fb_data,
+ fb_dev->width, fb_dev->height,
+ 32, fb_dev->stride,
+ 0x00ff0000,
+ 0x0000ff00,
+ 0x000000ff,
+ 0x00000000);
+ if (!fb_surface) {
+ fprintf(stderr, "Could not create SDL framebuffer surface\n");
+ exit(1);
+ }
+ }
+}
+
+static void sdl_update(FBDevice *fb_dev, void *opaque,
+ int x, int y, int w, int h)
+{
+ SDL_Rect r;
+ // printf("sdl_update: %d %d %d %d\n", x, y, w, h);
+ r.x = x;
+ r.y = y;
+ r.w = w;
+ r.h = h;
+ SDL_BlitSurface(fb_surface, &r, screen, &r);
+ SDL_UpdateRect(screen, r.x, r.y, r.w, r.h);
+}
+
+#if defined(_WIN32)
+
+static int sdl_get_keycode(const SDL_KeyboardEvent *ev)
+{
+ return ev->keysym.scancode;
+}
+
+#else
+
+/* we assume Xorg is used with a PC keyboard. Return 0 if no keycode found. */
+static int sdl_get_keycode(const SDL_KeyboardEvent *ev)
+{
+ int keycode;
+ keycode = ev->keysym.scancode;
+ if (keycode < 9) {
+ keycode = 0;
+ } else if (keycode < 127 + 8) {
+ keycode -= 8;
+ } else {
+ keycode = 0;
+ }
+ return keycode;
+}
+
+#endif
+
+/* release all pressed keys */
+static void sdl_reset_keys(VirtMachine *m)
+{
+ int i;
+
+ for(i = 1; i <= KEYCODE_MAX; i++) {
+ if (key_pressed[i]) {
+ vm_send_key_event(m, FALSE, i);
+ key_pressed[i] = FALSE;
+ }
+ }
+}
+
+static void sdl_handle_key_event(const SDL_KeyboardEvent *ev, VirtMachine *m)
+{
+ int keycode, keypress;
+
+ keycode = sdl_get_keycode(ev);
+ if (keycode) {
+ if (keycode == 0x3a || keycode ==0x45) {
+ /* SDL does not generate key up for numlock & caps lock */
+ vm_send_key_event(m, TRUE, keycode);
+ vm_send_key_event(m, FALSE, keycode);
+ } else {
+ keypress = (ev->type == SDL_KEYDOWN);
+ if (keycode <= KEYCODE_MAX)
+ key_pressed[keycode] = keypress;
+ vm_send_key_event(m, keypress, keycode);
+ }
+ } else if (ev->type == SDL_KEYUP) {
+ /* workaround to reset the keyboard state (used when changing
+ desktop with ctrl-alt-x on Linux) */
+ sdl_reset_keys(m);
+ }
+}
+
+static void sdl_send_mouse_event(VirtMachine *m, int x1, int y1,
+ int dz, int state, BOOL is_absolute)
+{
+ int buttons, x, y;
+
+ buttons = 0;
+ if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
+ buttons |= (1 << 0);
+ if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
+ buttons |= (1 << 1);
+ if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
+ buttons |= (1 << 2);
+ if (is_absolute) {
+ x = (x1 * 32768) / screen_width;
+ y = (y1 * 32768) / screen_height;
+ } else {
+ x = x1;
+ y = y1;
+ }
+ vm_send_mouse_event(m, x, y, dz, buttons);
+}
+
+static void sdl_handle_mouse_motion_event(const SDL_Event *ev, VirtMachine *m)
+{
+ BOOL is_absolute = vm_mouse_is_absolute(m);
+ int x, y;
+ if (is_absolute) {
+ x = ev->motion.x;
+ y = ev->motion.y;
+ } else {
+ x = ev->motion.xrel;
+ y = ev->motion.yrel;
+ }
+ sdl_send_mouse_event(m, x, y, 0, ev->motion.state, is_absolute);
+}
+
+static void sdl_handle_mouse_button_event(const SDL_Event *ev, VirtMachine *m)
+{
+ BOOL is_absolute = vm_mouse_is_absolute(m);
+ int state, dz;
+
+ dz = 0;
+ if (ev->type == SDL_MOUSEBUTTONDOWN) {
+ if (ev->button.button == SDL_BUTTON_WHEELUP) {
+ dz = 1;
+ } else if (ev->button.button == SDL_BUTTON_WHEELDOWN) {
+ dz = -1;
+ }
+ }
+
+ state = SDL_GetMouseState(NULL, NULL);
+ /* just in case */
+ if (ev->type == SDL_MOUSEBUTTONDOWN)
+ state |= SDL_BUTTON(ev->button.button);
+ else
+ state &= ~SDL_BUTTON(ev->button.button);
+
+ if (is_absolute) {
+ sdl_send_mouse_event(m, ev->button.x, ev->button.y,
+ dz, state, is_absolute);
+ } else {
+ sdl_send_mouse_event(m, 0, 0, dz, state, is_absolute);
+ }
+}
+
+void sdl_refresh(VirtMachine *m)
+{
+ SDL_Event ev_s, *ev = &ev_s;
+
+ if (!m->fb_dev)
+ return;
+
+ sdl_update_fb_surface(m->fb_dev);
+
+ m->fb_dev->refresh(m->fb_dev, sdl_update, NULL);
+
+ while (SDL_PollEvent(ev)) {
+ switch (ev->type) {
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ sdl_handle_key_event(&ev->key, m);
+ break;
+ case SDL_MOUSEMOTION:
+ sdl_handle_mouse_motion_event(ev, m);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ sdl_handle_mouse_button_event(ev, m);
+ break;
+ case SDL_QUIT:
+ exit(0);
+ }
+ }
+}
+
+static void sdl_hide_cursor(void)
+{
+ uint8_t data = 0;
+ sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
+ SDL_ShowCursor(1);
+ SDL_SetCursor(sdl_cursor_hidden);
+}
+
+void sdl_init(int width, int height)
+{
+ int flags;
+
+ screen_width = width;
+ screen_height = height;
+
+ if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE)) {
+ fprintf(stderr, "Could not initialize SDL - exiting\n");
+ exit(1);
+ }
+
+ flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
+ screen = SDL_SetVideoMode(width, height, 0, flags);
+ if (!screen || !screen->pixels) {
+ fprintf(stderr, "Could not open SDL display\n");
+ exit(1);
+ }
+
+ SDL_WM_SetCaption("TinyEMU", "TinyEMU");
+
+ sdl_hide_cursor();
+}
+
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/sha256.c b/jslinux-2019-12-21/tinyemu-2019-12-21/sha256.c
new file mode 100644
index 0000000..97bfbda
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/sha256.c
@@ -0,0 +1,341 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "cutils.h"
+#include "sha256.h"
+
+#define LOAD32H(a, b) a = get_be32(b)
+#define STORE32H(a, b) put_be32(b, a)
+#define STORE64H(a, b) put_be64(b, a)
+#define RORc(x, y) ( ((((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)((y)&31)) | ((uint32_t)(x)<<(uint32_t)(32-((y)&31)))) & 0xFFFFFFFFUL)
+
+#if defined(CONFIG_EMBUE)
+#define LTC_SMALL_CODE
+#endif
+
+/**
+ @file sha256.c
+ LTC_SHA256 by Tom St Denis
+*/
+
+#ifdef LTC_SMALL_CODE
+/* the K array */
+static const uint32_t K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+#endif
+
+/* Various logical functions */
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x),(n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+
+/* compress 512-bits */
+static void sha256_compress(SHA256_CTX *s, unsigned char *buf)
+{
+ uint32_t S[8], W[64], t0, t1;
+#ifdef LTC_SMALL_CODE
+ uint32_t t;
+#endif
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = s->state[i];
+ }
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD32H(W[i], buf + (4*i));
+ }
+
+ /* fill W[16..63] */
+ for (i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+ }
+
+ /* Compress */
+#ifdef LTC_SMALL_CODE
+#define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ for (i = 0; i < 64; ++i) {
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i);
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+#else
+#define RND(a,b,c,d,e,f,g,h,i,ki) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
+
+#undef RND
+
+#endif
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ s->state[i] = s->state[i] + S[i];
+ }
+}
+
+#ifdef LTC_CLEAN_STACK
+static int sha256_compress(hash_state * md, unsigned char *buf)
+{
+ int err;
+ err = _sha256_compress(md, buf);
+ burn_stack(sizeof(uint32_t) * 74);
+ return err;
+}
+#endif
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return CRYPT_OK if successful
+*/
+void SHA256_Init(SHA256_CTX *s)
+{
+ s->curlen = 0;
+ s->length = 0;
+ s->state[0] = 0x6A09E667UL;
+ s->state[1] = 0xBB67AE85UL;
+ s->state[2] = 0x3C6EF372UL;
+ s->state[3] = 0xA54FF53AUL;
+ s->state[4] = 0x510E527FUL;
+ s->state[5] = 0x9B05688CUL;
+ s->state[6] = 0x1F83D9ABUL;
+ s->state[7] = 0x5BE0CD19UL;
+}
+
+void SHA256_Update(SHA256_CTX *s, const uint8_t *in, unsigned long inlen)
+{
+ unsigned long n;
+
+ if (s->curlen > sizeof(s->buf)) {
+ abort();
+ }
+ if ((s->length + inlen) < s->length) {
+ abort();
+ }
+ while (inlen > 0) {
+ if (s->curlen == 0 && inlen >= 64) {
+ sha256_compress(s, (unsigned char *)in);
+ s->length += 64 * 8;
+ in += 64;
+ inlen -= 64;
+ } else {
+ n = min_int(inlen, 64 - s->curlen);
+ memcpy(s->buf + s->curlen, in, (size_t)n);
+ s->curlen += n;
+ in += n;
+ inlen -= n;
+ if (s->curlen == 64) {
+ sha256_compress(s, s->buf);
+ s->length += 8*64;
+ s->curlen = 0;
+ }
+ }
+ } }
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (32 bytes)
+ @return CRYPT_OK if successful
+*/
+void SHA256_Final(uint8_t *out, SHA256_CTX *s)
+{
+ int i;
+
+ if (s->curlen >= sizeof(s->buf)) {
+ abort();
+ }
+
+
+ /* increase the length of the message */
+ s->length += s->curlen * 8;
+
+ /* append the '1' bit */
+ s->buf[s->curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (s->curlen > 56) {
+ while (s->curlen < 64) {
+ s->buf[s->curlen++] = (unsigned char)0;
+ }
+ sha256_compress(s, s->buf);
+ s->curlen = 0;
+ }
+
+ /* pad upto 56 bytes of zeroes */
+ while (s->curlen < 56) {
+ s->buf[s->curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64H(s->length, s->buf+56);
+ sha256_compress(s, s->buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++) {
+ STORE32H(s->state[i], out+(4*i));
+ }
+#ifdef LTC_CLEAN_STACK
+ zeromem(md, sizeof(hash_state));
+#endif
+}
+
+void SHA256(const uint8_t *buf, int buf_len, uint8_t *out)
+{
+ SHA256_CTX ctx;
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, buf, buf_len);
+ SHA256_Final(out, &ctx);
+}
+
+#if 0
+/**
+ Self-test the hash
+ @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
+*/
+int sha256_test(void)
+{
+ #ifndef LTC_TEST
+ return CRYPT_NOP;
+ #else
+ static const struct {
+ char *msg;
+ unsigned char hash[32];
+ } tests[] = {
+ { "abc",
+ { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad }
+ },
+ { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+ 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+ 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+ 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 }
+ },
+ };
+
+ int i;
+ unsigned char tmp[32];
+ hash_state md;
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ sha256_init(&md);
+ sha256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
+ sha256_done(&md, tmp);
+ if (XMEMCMP(tmp, tests[i].hash, 32) != 0) {
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+ }
+ return CRYPT_OK;
+ #endif
+}
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/sha256.h b/jslinux-2019-12-21/tinyemu-2019-12-21/sha256.h
new file mode 100644
index 0000000..b43115f
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/sha256.h
@@ -0,0 +1,40 @@
+/*
+ * OpenSSL compatible SHA256 header
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef SHA256_H
+#define SHA256_H
+
+#define SHA256_DIGEST_LENGTH 32
+
+typedef struct {
+ uint64_t length;
+ uint32_t state[8], curlen;
+ uint8_t buf[64];
+} SHA256_CTX;
+
+void SHA256_Init(SHA256_CTX *s);
+void SHA256_Update(SHA256_CTX *s, const uint8_t *in, unsigned long inlen);
+void SHA256_Final(uint8_t *out, SHA256_CTX *s);
+void SHA256(const uint8_t *buf, int buf_len, uint8_t *out);
+
+#endif /* SHA256_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/simplefb.c b/jslinux-2019-12-21/tinyemu-2019-12-21/simplefb.c
new file mode 100644
index 0000000..5664cea
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/simplefb.c
@@ -0,0 +1,127 @@
+/*
+ * Simple frame buffer
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "virtio.h"
+#include "machine.h"
+
+//#define DEBUG_VBE
+
+#define FB_ALLOC_ALIGN 65536
+
+struct SimpleFBState {
+ FBDevice *fb_dev;
+ int fb_page_count;
+ PhysMemoryRange *mem_range;
+};
+
+#define MAX_MERGE_DISTANCE 3
+
+void simplefb_refresh(FBDevice *fb_dev,
+ SimpleFBDrawFunc *redraw_func, void *opaque,
+ PhysMemoryRange *mem_range,
+ int fb_page_count)
+{
+ const uint32_t *dirty_bits;
+ uint32_t dirty_val;
+ int y0, y1, page_y0, page_y1, byte_pos, page_index, bit_pos;
+
+ dirty_bits = phys_mem_get_dirty_bits(mem_range);
+
+ page_index = 0;
+ y0 = y1 = 0;
+ while (page_index < fb_page_count) {
+ dirty_val = dirty_bits[page_index >> 5];
+ if (dirty_val != 0) {
+ bit_pos = 0;
+ while (dirty_val != 0) {
+ while (((dirty_val >> bit_pos) & 1) == 0)
+ bit_pos++;
+ dirty_val &= ~(1 << bit_pos);
+
+ byte_pos = (page_index + bit_pos) * DEVRAM_PAGE_SIZE;
+ page_y0 = byte_pos / fb_dev->stride;
+ page_y1 = ((byte_pos + DEVRAM_PAGE_SIZE - 1) / fb_dev->stride) + 1;
+ page_y1 = min_int(page_y1, fb_dev->height);
+ if (y0 == y1) {
+ y0 = page_y0;
+ y1 = page_y1;
+ } else if (page_y0 <= (y1 + MAX_MERGE_DISTANCE)) {
+ /* union with current region */
+ y1 = page_y1;
+ } else {
+ /* flush */
+ redraw_func(fb_dev, opaque,
+ 0, y0, fb_dev->width, y1 - y0);
+ y0 = page_y0;
+ y1 = page_y1;
+ }
+ }
+ }
+ page_index += 32;
+ }
+
+ if (y0 != y1) {
+ redraw_func(fb_dev, opaque,
+ 0, y0, fb_dev->width, y1 - y0);
+ }
+}
+
+static void simplefb_refresh1(FBDevice *fb_dev,
+ SimpleFBDrawFunc *redraw_func, void *opaque)
+{
+ SimpleFBState *s = fb_dev->device_opaque;
+ simplefb_refresh(fb_dev, redraw_func, opaque, s->mem_range,
+ s->fb_page_count);
+}
+
+SimpleFBState *simplefb_init(PhysMemoryMap *map, uint64_t phys_addr,
+ FBDevice *fb_dev, int width, int height)
+{
+ SimpleFBState *s;
+
+ s = mallocz(sizeof(*s));
+ s->fb_dev = fb_dev;
+
+ fb_dev->width = width;
+ fb_dev->height = height;
+ fb_dev->stride = width * 4;
+ fb_dev->fb_size = (height * fb_dev->stride + FB_ALLOC_ALIGN - 1) & ~(FB_ALLOC_ALIGN - 1);
+ s->fb_page_count = fb_dev->fb_size >> DEVRAM_PAGE_SIZE_LOG2;
+
+ s->mem_range = cpu_register_ram(map, phys_addr, fb_dev->fb_size,
+ DEVRAM_FLAG_DIRTY_BITS);
+
+ fb_dev->fb_data = s->mem_range->phys_mem;
+ fb_dev->device_opaque = s;
+ fb_dev->refresh = simplefb_refresh1;
+ return s;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/bootp.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/bootp.c
new file mode 100644
index 0000000..3e5f457
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/bootp.c
@@ -0,0 +1,314 @@
+/*
+ * QEMU BOOTP/DHCP server
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "slirp.h"
+
+/* XXX: only DHCP is supported */
+
+#define LEASE_TIME (24 * 3600)
+
+static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) \
+do if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } while (0)
+#else
+#define DPRINTF(fmt, ...) do{}while(0)
+#endif
+
+static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ BOOTPClient *bc;
+ int i;
+
+ for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
+ bc = &slirp->bootp_clients[i];
+ if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6))
+ goto found;
+ }
+ return NULL;
+ found:
+ bc = &slirp->bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
+ return bc;
+}
+
+static BOOTPClient *request_addr(Slirp *slirp, const struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ uint32_t req_addr = ntohl(paddr->s_addr);
+ uint32_t dhcp_addr = ntohl(slirp->vdhcp_startaddr.s_addr);
+ BOOTPClient *bc;
+
+ if (req_addr >= dhcp_addr &&
+ req_addr < (dhcp_addr + NB_BOOTP_CLIENTS)) {
+ bc = &slirp->bootp_clients[req_addr - dhcp_addr];
+ if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
+ bc->allocated = 1;
+ return bc;
+ }
+ }
+ return NULL;
+}
+
+static BOOTPClient *find_addr(Slirp *slirp, struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ BOOTPClient *bc;
+ int i;
+
+ for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
+ if (!memcmp(macaddr, slirp->bootp_clients[i].macaddr, 6))
+ goto found;
+ }
+ return NULL;
+ found:
+ bc = &slirp->bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
+ return bc;
+}
+
+static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
+ struct in_addr *preq_addr)
+{
+ const uint8_t *p, *p_end;
+ int len, tag;
+
+ *pmsg_type = 0;
+ preq_addr->s_addr = htonl(0L);
+
+ p = bp->bp_vend;
+ p_end = p + DHCP_OPT_LEN;
+ if (memcmp(p, rfc1533_cookie, 4) != 0)
+ return;
+ p += 4;
+ while (p < p_end) {
+ tag = p[0];
+ if (tag == RFC1533_PAD) {
+ p++;
+ } else if (tag == RFC1533_END) {
+ break;
+ } else {
+ p++;
+ if (p >= p_end)
+ break;
+ len = *p++;
+ DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
+
+ switch(tag) {
+ case RFC2132_MSG_TYPE:
+ if (len >= 1)
+ *pmsg_type = p[0];
+ break;
+ case RFC2132_REQ_ADDR:
+ if (len >= 4) {
+ memcpy(&(preq_addr->s_addr), p, 4);
+ }
+ break;
+ default:
+ break;
+ }
+ p += len;
+ }
+ }
+ if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) &&
+ bp->bp_ciaddr.s_addr) {
+ memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4);
+ }
+}
+
+static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
+{
+ BOOTPClient *bc = NULL;
+ struct mbuf *m;
+ struct bootp_t *rbp;
+ struct sockaddr_in saddr, daddr;
+ struct in_addr preq_addr;
+ int dhcp_msg_type, val;
+ uint8_t *q;
+
+ /* extract exact DHCP msg type */
+ dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
+ DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
+ if (preq_addr.s_addr != htonl(0L))
+ DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr));
+ else
+ DPRINTF("\n");
+
+ if (dhcp_msg_type == 0)
+ dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
+
+ if (dhcp_msg_type != DHCPDISCOVER &&
+ dhcp_msg_type != DHCPREQUEST)
+ return;
+ /* XXX: this is a hack to get the client mac address */
+ memcpy(slirp->client_ethaddr, bp->bp_hwaddr, 6);
+
+ m = m_get(slirp);
+ if (!m) {
+ return;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ rbp = (struct bootp_t *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+ memset(rbp, 0, sizeof(struct bootp_t));
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ if (preq_addr.s_addr != htonl(0L)) {
+ bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr);
+ if (bc) {
+ daddr.sin_addr = preq_addr;
+ }
+ }
+ if (!bc) {
+ new_addr:
+ bc = get_new_addr(slirp, &daddr.sin_addr, slirp->client_ethaddr);
+ if (!bc) {
+ DPRINTF("no address left\n");
+ return;
+ }
+ }
+ memcpy(bc->macaddr, slirp->client_ethaddr, 6);
+ } else if (preq_addr.s_addr != htonl(0L)) {
+ bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr);
+ if (bc) {
+ daddr.sin_addr = preq_addr;
+ memcpy(bc->macaddr, slirp->client_ethaddr, 6);
+ } else {
+ daddr.sin_addr.s_addr = 0;
+ }
+ } else {
+ bc = find_addr(slirp, &daddr.sin_addr, bp->bp_hwaddr);
+ if (!bc) {
+ /* if never assigned, behaves as if it was already
+ assigned (windows fix because it remembers its address) */
+ goto new_addr;
+ }
+ }
+
+ saddr.sin_addr = slirp->vhost_addr;
+ saddr.sin_port = htons(BOOTP_SERVER);
+
+ daddr.sin_port = htons(BOOTP_CLIENT);
+
+ rbp->bp_op = BOOTP_REPLY;
+ rbp->bp_xid = bp->bp_xid;
+ rbp->bp_htype = 1;
+ rbp->bp_hlen = 6;
+ memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
+
+ rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
+ rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
+
+ q = rbp->bp_vend;
+ memcpy(q, rfc1533_cookie, 4);
+ q += 4;
+
+ if (bc) {
+ DPRINTF("%s addr=%08x\n",
+ (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
+ ntohl(daddr.sin_addr.s_addr));
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPOFFER;
+ } else /* DHCPREQUEST */ {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPACK;
+ }
+
+ if (slirp->bootp_filename)
+ snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
+ slirp->bootp_filename);
+
+ *q++ = RFC2132_SRV_ID;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_NETMASK;
+ *q++ = 4;
+ memcpy(q, &slirp->vnetwork_mask, 4);
+ q += 4;
+
+ if (!slirp->restricted) {
+ *q++ = RFC1533_GATEWAY;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_DNS;
+ *q++ = 4;
+ memcpy(q, &slirp->vnameserver_addr, 4);
+ q += 4;
+ }
+
+ *q++ = RFC2132_LEASE_TIME;
+ *q++ = 4;
+ val = htonl(LEASE_TIME);
+ memcpy(q, &val, 4);
+ q += 4;
+
+ if (*slirp->client_hostname) {
+ val = strlen(slirp->client_hostname);
+ *q++ = RFC1533_HOSTNAME;
+ *q++ = val;
+ memcpy(q, slirp->client_hostname, val);
+ q += val;
+ }
+ } else {
+ static const char nak_msg[] = "requested address not available";
+
+ DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr->s_addr));
+
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPNAK;
+
+ *q++ = RFC2132_MESSAGE;
+ *q++ = sizeof(nak_msg) - 1;
+ memcpy(q, nak_msg, sizeof(nak_msg) - 1);
+ q += sizeof(nak_msg) - 1;
+ }
+ *q = RFC1533_END;
+
+ daddr.sin_addr.s_addr = 0xffffffffu;
+
+ m->m_len = sizeof(struct bootp_t) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+}
+
+void bootp_input(struct mbuf *m)
+{
+ struct bootp_t *bp = mtod(m, struct bootp_t *);
+
+ if (bp->bp_op == BOOTP_REQUEST) {
+ bootp_reply(m->slirp, bp);
+ }
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/bootp.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/bootp.h
new file mode 100644
index 0000000..30c30ab
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/bootp.h
@@ -0,0 +1,122 @@
+/* bootp/dhcp defines */
+
+#define BOOTP_SERVER 67
+#define BOOTP_CLIENT 68
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+#define RFC1533_COOKIE 99, 130, 83, 99
+#define RFC1533_PAD 0
+#define RFC1533_NETMASK 1
+#define RFC1533_TIMEOFFSET 2
+#define RFC1533_GATEWAY 3
+#define RFC1533_TIMESERVER 4
+#define RFC1533_IEN116NS 5
+#define RFC1533_DNS 6
+#define RFC1533_LOGSERVER 7
+#define RFC1533_COOKIESERVER 8
+#define RFC1533_LPRSERVER 9
+#define RFC1533_IMPRESSSERVER 10
+#define RFC1533_RESOURCESERVER 11
+#define RFC1533_HOSTNAME 12
+#define RFC1533_BOOTFILESIZE 13
+#define RFC1533_MERITDUMPFILE 14
+#define RFC1533_DOMAINNAME 15
+#define RFC1533_SWAPSERVER 16
+#define RFC1533_ROOTPATH 17
+#define RFC1533_EXTENSIONPATH 18
+#define RFC1533_IPFORWARDING 19
+#define RFC1533_IPSOURCEROUTING 20
+#define RFC1533_IPPOLICYFILTER 21
+#define RFC1533_IPMAXREASSEMBLY 22
+#define RFC1533_IPTTL 23
+#define RFC1533_IPMTU 24
+#define RFC1533_IPMTUPLATEAU 25
+#define RFC1533_INTMTU 26
+#define RFC1533_INTLOCALSUBNETS 27
+#define RFC1533_INTBROADCAST 28
+#define RFC1533_INTICMPDISCOVER 29
+#define RFC1533_INTICMPRESPOND 30
+#define RFC1533_INTROUTEDISCOVER 31
+#define RFC1533_INTROUTESOLICIT 32
+#define RFC1533_INTSTATICROUTES 33
+#define RFC1533_LLTRAILERENCAP 34
+#define RFC1533_LLARPCACHETMO 35
+#define RFC1533_LLETHERNETENCAP 36
+#define RFC1533_TCPTTL 37
+#define RFC1533_TCPKEEPALIVETMO 38
+#define RFC1533_TCPKEEPALIVEGB 39
+#define RFC1533_NISDOMAIN 40
+#define RFC1533_NISSERVER 41
+#define RFC1533_NTPSERVER 42
+#define RFC1533_VENDOR 43
+#define RFC1533_NBNS 44
+#define RFC1533_NBDD 45
+#define RFC1533_NBNT 46
+#define RFC1533_NBSCOPE 47
+#define RFC1533_XFS 48
+#define RFC1533_XDM 49
+
+#define RFC2132_REQ_ADDR 50
+#define RFC2132_LEASE_TIME 51
+#define RFC2132_MSG_TYPE 53
+#define RFC2132_SRV_ID 54
+#define RFC2132_PARAM_LIST 55
+#define RFC2132_MESSAGE 56
+#define RFC2132_MAX_SIZE 57
+#define RFC2132_RENEWAL_TIME 58
+#define RFC2132_REBIND_TIME 59
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPACK 5
+#define DHCPNAK 6
+
+#define RFC1533_VENDOR_MAJOR 0
+#define RFC1533_VENDOR_MINOR 0
+
+#define RFC1533_VENDOR_MAGIC 128
+#define RFC1533_VENDOR_ADDPARM 129
+#define RFC1533_VENDOR_ETHDEV 130
+#define RFC1533_VENDOR_HOWTO 132
+#define RFC1533_VENDOR_MNUOPTS 160
+#define RFC1533_VENDOR_SELECTION 176
+#define RFC1533_VENDOR_MOTD 184
+#define RFC1533_VENDOR_NUMOFMOTD 8
+#define RFC1533_VENDOR_IMG 192
+#define RFC1533_VENDOR_NUMOFIMG 16
+
+#define RFC1533_END 255
+#define BOOTP_VENDOR_LEN 64
+#define DHCP_OPT_LEN 312
+
+struct bootp_t {
+ struct ip ip;
+ struct udphdr udp;
+ uint8_t bp_op;
+ uint8_t bp_htype;
+ uint8_t bp_hlen;
+ uint8_t bp_hops;
+ uint32_t bp_xid;
+ uint16_t bp_secs;
+ uint16_t unused;
+ struct in_addr bp_ciaddr;
+ struct in_addr bp_yiaddr;
+ struct in_addr bp_siaddr;
+ struct in_addr bp_giaddr;
+ uint8_t bp_hwaddr[16];
+ uint8_t bp_sname[64];
+ uint8_t bp_file[128];
+ uint8_t bp_vend[DHCP_OPT_LEN];
+};
+
+typedef struct {
+ uint16_t allocated;
+ uint8_t macaddr[6];
+} BOOTPClient;
+
+#define NB_BOOTP_CLIENTS 16
+
+void bootp_input(struct mbuf *m);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/cksum.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/cksum.c
new file mode 100644
index 0000000..5aa9060
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/cksum.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp
+ */
+
+#include "slirp.h"
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ *
+ * XXX Since we will never span more than 1 mbuf, we can optimise this
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; \
+ (void)ADDCARRY(sum);}
+
+int cksum(struct mbuf *m, int len)
+{
+ register uint16_t *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+
+ union {
+ uint8_t c[2];
+ uint16_t s;
+ } s_util;
+ union {
+ uint16_t s[2];
+ uint32_t l;
+ } l_util;
+
+ if (m->m_len == 0)
+ goto cont;
+ w = mtod(m, uint16_t *);
+
+ mlen = m->m_len;
+
+ if (len < mlen)
+ mlen = len;
+#ifdef DEBUG
+ len -= mlen;
+#endif
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(uint8_t *)w;
+ w = (uint16_t *)((int8_t *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ goto cont;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ if (mlen == -1) {
+ s_util.c[1] = *(uint8_t *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(uint8_t *)w;
+
+cont:
+#ifdef DEBUG
+ if (len) {
+ DEBUG_ERROR((dfd, "cksum: out of data\n"));
+ DEBUG_ERROR((dfd, " len = %d\n", len));
+ }
+#endif
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/debug.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/debug.h
new file mode 100644
index 0000000..6cfa61e
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/debug.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+
+#define DBG_CALL 0x1
+#define DBG_MISC 0x2
+#define DBG_ERROR 0x4
+
+#define dfd stderr
+
+extern int slirp_debug;
+
+#define DEBUG_CALL(x) if (slirp_debug & DBG_CALL) { fprintf(dfd, "%s...\n", x); fflush(dfd); }
+#define DEBUG_ARG(x, y) if (slirp_debug & DBG_CALL) { fputc(' ', dfd); fprintf(dfd, x, y); fputc('\n', dfd); fflush(dfd); }
+#define DEBUG_ARGS(x) if (slirp_debug & DBG_CALL) { fprintf x ; fflush(dfd); }
+#define DEBUG_MISC(x) if (slirp_debug & DBG_MISC) { fprintf x ; fflush(dfd); }
+#define DEBUG_ERROR(x) if (slirp_debug & DBG_ERROR) {fprintf x ; fflush(dfd); }
+
+#else
+
+#define DEBUG_CALL(x)
+#define DEBUG_ARG(x, y)
+#define DEBUG_ARGS(x)
+#define DEBUG_MISC(x)
+#define DEBUG_ERROR(x)
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/if.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/if.c
new file mode 100644
index 0000000..e6d114a
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/if.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+static void
+ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
+{
+ ifm->ifs_next = ifmhead->ifs_next;
+ ifmhead->ifs_next = ifm;
+ ifm->ifs_prev = ifmhead;
+ ifm->ifs_next->ifs_prev = ifm;
+}
+
+static void
+ifs_remque(struct mbuf *ifm)
+{
+ ifm->ifs_prev->ifs_next = ifm->ifs_next;
+ ifm->ifs_next->ifs_prev = ifm->ifs_prev;
+}
+
+void
+if_init(Slirp *slirp)
+{
+ slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq;
+ slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq;
+ slirp->next_m = &slirp->if_batchq;
+}
+
+/*
+ * if_output: Queue packet into an output queue.
+ * There are 2 output queue's, if_fastq and if_batchq.
+ * Each output queue is a doubly linked list of double linked lists
+ * of mbufs, each list belonging to one "session" (socket). This
+ * way, we can output packets fairly by sending one packet from each
+ * session, instead of all the packets from one session, then all packets
+ * from the next session, etc. Packets on the if_fastq get absolute
+ * priority, but if one session hogs the link, it gets "downgraded"
+ * to the batchq until it runs out of packets, then it'll return
+ * to the fastq (eg. if the user does an ls -alR in a telnet session,
+ * it'll temporarily get downgraded to the batchq)
+ */
+void
+if_output(struct socket *so, struct mbuf *ifm)
+{
+ Slirp *slirp = ifm->slirp;
+ struct mbuf *ifq;
+ int on_fastq = 1;
+
+ DEBUG_CALL("if_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("ifm = %lx", (long)ifm);
+
+ /*
+ * First remove the mbuf from m_usedlist,
+ * since we're gonna use m_next and m_prev ourselves
+ * XXX Shouldn't need this, gotta change dtom() etc.
+ */
+ if (ifm->m_flags & M_USEDLIST) {
+ remque(ifm);
+ ifm->m_flags &= ~M_USEDLIST;
+ }
+
+ /*
+ * See if there's already a batchq list for this session.
+ * This can include an interactive session, which should go on fastq,
+ * but gets too greedy... hence it'll be downgraded from fastq to batchq.
+ * We mustn't put this packet back on the fastq (or we'll send it out of order)
+ * XXX add cache here?
+ */
+ for (ifq = slirp->if_batchq.ifq_prev; ifq != &slirp->if_batchq;
+ ifq = ifq->ifq_prev) {
+ if (so == ifq->ifq_so) {
+ /* A match! */
+ ifm->ifq_so = so;
+ ifs_insque(ifm, ifq->ifs_prev);
+ goto diddit;
+ }
+ }
+
+ /* No match, check which queue to put it on */
+ if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
+ ifq = slirp->if_fastq.ifq_prev;
+ on_fastq = 1;
+ /*
+ * Check if this packet is a part of the last
+ * packet's session
+ */
+ if (ifq->ifq_so == so) {
+ ifm->ifq_so = so;
+ ifs_insque(ifm, ifq->ifs_prev);
+ goto diddit;
+ }
+ } else
+ ifq = slirp->if_batchq.ifq_prev;
+
+ /* Create a new doubly linked list for this session */
+ ifm->ifq_so = so;
+ ifs_init(ifm);
+ insque(ifm, ifq);
+
+diddit:
+ slirp->if_queued++;
+
+ if (so) {
+ /* Update *_queued */
+ so->so_queued++;
+ so->so_nqueued++;
+ /*
+ * Check if the interactive session should be downgraded to
+ * the batchq. A session is downgraded if it has queued 6
+ * packets without pausing, and at least 3 of those packets
+ * have been sent over the link
+ * (XXX These are arbitrary numbers, probably not optimal..)
+ */
+ if (on_fastq && ((so->so_nqueued >= 6) &&
+ (so->so_nqueued - so->so_queued) >= 3)) {
+
+ /* Remove from current queue... */
+ remque(ifm->ifs_next);
+
+ /* ...And insert in the new. That'll teach ya! */
+ insque(ifm->ifs_next, &slirp->if_batchq);
+ }
+ }
+
+#ifndef FULL_BOLT
+ /*
+ * This prevents us from malloc()ing too many mbufs
+ */
+ if_start(ifm->slirp);
+#endif
+}
+
+/*
+ * Send a packet
+ * We choose a packet based on it's position in the output queues;
+ * If there are packets on the fastq, they are sent FIFO, before
+ * everything else. Otherwise we choose the first packet from the
+ * batchq and send it. the next packet chosen will be from the session
+ * after this one, then the session after that one, and so on.. So,
+ * for example, if there are 3 ftp session's fighting for bandwidth,
+ * one packet will be sent from the first session, then one packet
+ * from the second session, then one packet from the third, then back
+ * to the first, etc. etc.
+ */
+void
+if_start(Slirp *slirp)
+{
+ struct mbuf *ifm, *ifqt;
+
+ DEBUG_CALL("if_start");
+
+ if (slirp->if_queued == 0)
+ return; /* Nothing to do */
+
+ again:
+ /* check if we can really output */
+ if (!slirp_can_output(slirp->opaque))
+ return;
+
+ /*
+ * See which queue to get next packet from
+ * If there's something in the fastq, select it immediately
+ */
+ if (slirp->if_fastq.ifq_next != &slirp->if_fastq) {
+ ifm = slirp->if_fastq.ifq_next;
+ } else {
+ /* Nothing on fastq, see if next_m is valid */
+ if (slirp->next_m != &slirp->if_batchq)
+ ifm = slirp->next_m;
+ else
+ ifm = slirp->if_batchq.ifq_next;
+
+ /* Set which packet to send on next iteration */
+ slirp->next_m = ifm->ifq_next;
+ }
+ /* Remove it from the queue */
+ ifqt = ifm->ifq_prev;
+ remque(ifm);
+ slirp->if_queued--;
+
+ /* If there are more packets for this session, re-queue them */
+ if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
+ insque(ifm->ifs_next, ifqt);
+ ifs_remque(ifm);
+ }
+
+ /* Update so_queued */
+ if (ifm->ifq_so) {
+ if (--ifm->ifq_so->so_queued == 0)
+ /* If there's no more queued, reset nqueued */
+ ifm->ifq_so->so_nqueued = 0;
+ }
+
+ /* Encapsulate the packet for sending */
+ if_encap(slirp, (uint8_t *)ifm->m_data, ifm->m_len);
+
+ m_free(ifm);
+
+ if (slirp->if_queued)
+ goto again;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/if.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/if.h
new file mode 100644
index 0000000..2dac1c7
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/if.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _IF_H_
+#define _IF_H_
+
+#define IF_COMPRESS 0x01 /* We want compression */
+#define IF_NOCOMPRESS 0x02 /* Do not do compression */
+#define IF_AUTOCOMP 0x04 /* Autodetect (default) */
+#define IF_NOCIDCOMP 0x08 /* CID compression */
+
+#define IF_MTU 1500
+#define IF_MRU 1500
+#define IF_COMP IF_AUTOCOMP /* Flags for compression */
+
+/* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
+#define IF_MAXLINKHDR (2 + 14 + 40)
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip.h
new file mode 100644
index 0000000..48ea38e
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip.h 8.1 (Berkeley) 6/10/93
+ * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp
+ */
+
+#ifndef _IP_H_
+#define _IP_H_
+
+#ifdef HOST_WORDS_BIGENDIAN
+# ifndef NTOHL
+# define NTOHL(d)
+# endif
+# ifndef NTOHS
+# define NTOHS(d)
+# endif
+# ifndef HTONL
+# define HTONL(d)
+# endif
+# ifndef HTONS
+# define HTONS(d)
+# endif
+#else
+# ifndef NTOHL
+# define NTOHL(d) ((d) = ntohl((d)))
+# endif
+# ifndef NTOHS
+# define NTOHS(d) ((d) = ntohs((uint16_t)(d)))
+# endif
+# ifndef HTONL
+# define HTONL(d) ((d) = htonl((d)))
+# endif
+# ifndef HTONS
+# define HTONS(d) ((d) = htons((uint16_t)(d)))
+# endif
+#endif
+
+typedef uint32_t n_long; /* long as received from the net */
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ */
+struct ip {
+#ifdef HOST_WORDS_BIGENDIAN
+ u_int ip_v:4, /* version */
+ ip_hl:4; /* header length */
+#else
+ u_int ip_hl:4, /* header length */
+ ip_v:4; /* version */
+#endif
+ uint8_t ip_tos; /* type of service */
+ uint16_t ip_len; /* total length */
+ uint16_t ip_id; /* identification */
+ uint16_t ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* don't fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ uint8_t ip_ttl; /* time to live */
+ uint8_t ip_p; /* protocol */
+ uint16_t ip_sum; /* checksum */
+ struct in_addr ip_src,ip_dst; /* source and dest address */
+} __attribute__((packed));
+
+#define IP_MAXPACKET 65535 /* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+
+/*
+ * Definitions for options.
+ */
+#define IPOPT_COPIED(o) ((o)&0x80)
+#define IPOPT_CLASS(o) ((o)&0x60)
+#define IPOPT_NUMBER(o) ((o)&0x1f)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_DEBMEAS 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_EOL 0 /* end of option list */
+#define IPOPT_NOP 1 /* no operation */
+
+#define IPOPT_RR 7 /* record packet route */
+#define IPOPT_TS 68 /* timestamp */
+#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
+#define IPOPT_LSRR 131 /* loose source route */
+#define IPOPT_SATID 136 /* satnet id */
+#define IPOPT_SSRR 137 /* strict source route */
+
+/*
+ * Offsets to fields in options other than EOL and NOP.
+ */
+#define IPOPT_OPTVAL 0 /* option ID */
+#define IPOPT_OLEN 1 /* option length */
+#define IPOPT_OFFSET 2 /* offset within option */
+#define IPOPT_MINOFF 4 /* min value of above */
+
+/*
+ * Time stamp option structure.
+ */
+struct ip_timestamp {
+ uint8_t ipt_code; /* IPOPT_TS */
+ uint8_t ipt_len; /* size of structure (variable) */
+ uint8_t ipt_ptr; /* index of current entry */
+#ifdef HOST_WORDS_BIGENDIAN
+ u_int ipt_oflw:4, /* overflow counter */
+ ipt_flg:4; /* flags, see below */
+#else
+ u_int ipt_flg:4, /* flags, see below */
+ ipt_oflw:4; /* overflow counter */
+#endif
+ union ipt_timestamp {
+ n_long ipt_time[1];
+ struct ipt_ta {
+ struct in_addr ipt_addr;
+ n_long ipt_time;
+ } ipt_ta[1];
+ } ipt_timestamp;
+} __attribute__((packed));
+
+/* flag bits for ipt_flg */
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+/* bits for security (not byte swapped) */
+#define IPOPT_SECUR_UNCLASS 0x0000
+#define IPOPT_SECUR_CONFID 0xf135
+#define IPOPT_SECUR_EFTO 0x789a
+#define IPOPT_SECUR_MMMM 0xbc4d
+#define IPOPT_SECUR_RESTR 0xaf13
+#define IPOPT_SECUR_SECRET 0xd788
+#define IPOPT_SECUR_TOPSECRET 0x6bc5
+
+/*
+ * Internet implementation parameters.
+ */
+#define MAXTTL 255 /* maximum time to live (seconds) */
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#define IPFRAGTTL 60 /* time to live for frags, slowhz */
+#define IPTTLDEC 1 /* subtracted when forwarding */
+
+#define IP_MSS 576 /* default maximum segment size */
+
+#if SIZEOF_CHAR_P == 4
+struct mbuf_ptr {
+ struct mbuf *mptr;
+ uint32_t dummy;
+} __attribute__((packed));
+#else
+struct mbuf_ptr {
+ struct mbuf *mptr;
+} __attribute__((packed));
+#endif
+struct qlink {
+ void *next, *prev;
+};
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+ struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */
+ uint8_t ih_x1; /* (unused) */
+ uint8_t ih_pr; /* protocol */
+ uint16_t ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+} __attribute__((packed));
+
+/*
+ * Ip reassembly queue structure. Each fragment
+ * being reassembled is attached to one of these structures.
+ * They are timed out after ipq_ttl drops to 0, and may also
+ * be reclaimed if memory becomes tight.
+ * size 28 bytes
+ */
+struct ipq {
+ struct qlink frag_link; /* to ip headers of fragments */
+ struct qlink ip_link; /* to other reass headers */
+ uint8_t ipq_ttl; /* time for reass q to live */
+ uint8_t ipq_p; /* protocol of this fragment */
+ uint16_t ipq_id; /* sequence id for reassembly */
+ struct in_addr ipq_src,ipq_dst;
+} __attribute__((packed));
+
+/*
+ * Ip header, when holding a fragment.
+ *
+ * Note: ipf_link must be at same offset as frag_link above
+ */
+struct ipasfrag {
+ struct qlink ipf_link;
+ struct ip ipf_ip;
+} __attribute__((packed));
+
+#define ipf_off ipf_ip.ip_off
+#define ipf_tos ipf_ip.ip_tos
+#define ipf_len ipf_ip.ip_len
+#define ipf_next ipf_link.next
+#define ipf_prev ipf_link.prev
+
+/*
+ * Structure stored in mbuf in inpcb.ip_options
+ * and passed to ip_output when ip options are in use.
+ * The actual length of the options (including ipopt_dst)
+ * is in m_len.
+ */
+#define MAX_IPOPTLEN 40
+
+struct ipoption {
+ struct in_addr ipopt_dst; /* first-hop dst if source routed */
+ int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */
+} __attribute__((packed));
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_icmp.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_icmp.c
new file mode 100644
index 0000000..751a8e2
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_icmp.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
+ * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+/* The message sent when emulating PING */
+/* Be nice and tell them it's just a pseudo-ping packet */
+static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
+
+/* list of actions for icmp_error() on RX of an icmp message */
+static const int icmp_flush[19] = {
+/* ECHO REPLY (0) */ 0,
+ 1,
+ 1,
+/* DEST UNREACH (3) */ 1,
+/* SOURCE QUENCH (4)*/ 1,
+/* REDIRECT (5) */ 1,
+ 1,
+ 1,
+/* ECHO (8) */ 0,
+/* ROUTERADVERT (9) */ 1,
+/* ROUTERSOLICIT (10) */ 1,
+/* TIME EXCEEDED (11) */ 1,
+/* PARAMETER PROBLEM (12) */ 1,
+/* TIMESTAMP (13) */ 0,
+/* TIMESTAMP REPLY (14) */ 0,
+/* INFO (15) */ 0,
+/* INFO REPLY (16) */ 0,
+/* ADDR MASK (17) */ 0,
+/* ADDR MASK REPLY (18) */ 0
+};
+
+/*
+ * Process a received ICMP message.
+ */
+void
+icmp_input(struct mbuf *m, int hlen)
+{
+ register struct icmp *icp;
+ register struct ip *ip=mtod(m, struct ip *);
+ int icmplen=ip->ip_len;
+ Slirp *slirp = m->slirp;
+
+ DEBUG_CALL("icmp_input");
+ DEBUG_ARG("m = %lx", (long )m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ /*
+ * Locate icmp structure in mbuf, and check
+ * that its not corrupted and of at least minimum length.
+ */
+ if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */
+ freeit:
+ m_freem(m);
+ goto end_error;
+ }
+
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ icp = mtod(m, struct icmp *);
+ if (cksum(m, icmplen)) {
+ goto freeit;
+ }
+ m->m_len += hlen;
+ m->m_data -= hlen;
+
+ DEBUG_ARG("icmp_type = %d", icp->icmp_type);
+ switch (icp->icmp_type) {
+ case ICMP_ECHO:
+ icp->icmp_type = ICMP_ECHOREPLY;
+ ip->ip_len += hlen; /* since ip_input subtracts this */
+ if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
+ icmp_reflect(m);
+ } else {
+ struct socket *so;
+ struct sockaddr_in addr;
+ if ((so = socreate(slirp)) == NULL) goto freeit;
+ if(udp_attach(so) == -1) {
+ DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
+ errno,strerror(errno)));
+ sofree(so);
+ m_free(m);
+ goto end_error;
+ }
+ so->so_m = m;
+ so->so_faddr = ip->ip_dst;
+ so->so_fport = htons(7);
+ so->so_laddr = ip->ip_src;
+ so->so_lport = htons(9);
+ so->so_iptos = ip->ip_tos;
+ so->so_type = IPPROTO_ICMP;
+ so->so_state = SS_ISFCONNECTED;
+
+ /* Send the packet */
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ /* It's an alias */
+ if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
+ if (get_dns_addr(&addr.sin_addr) < 0)
+ addr.sin_addr = loopback_addr;
+ } else {
+ addr.sin_addr = loopback_addr;
+ }
+ } else {
+ addr.sin_addr = so->so_faddr;
+ }
+ addr.sin_port = so->so_fport;
+ if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
+ (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
+ errno,strerror(errno)));
+ icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ udp_detach(so);
+ }
+ } /* if ip->ip_dst.s_addr == alias_addr.s_addr */
+ break;
+ case ICMP_UNREACH:
+ /* XXX? report error? close socket? */
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ case ICMP_SOURCEQUENCH:
+ case ICMP_TSTAMP:
+ case ICMP_MASKREQ:
+ case ICMP_REDIRECT:
+ m_freem(m);
+ break;
+
+ default:
+ m_freem(m);
+ } /* swith */
+
+end_error:
+ /* m is m_free()'d xor put in a socket xor or given to ip_send */
+ return;
+}
+
+
+/*
+ * Send an ICMP message in response to a situation
+ *
+ * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
+ * MUST NOT change this header information.
+ * MUST NOT reply to a multicast/broadcast IP address.
+ * MUST NOT reply to a multicast/broadcast MAC address.
+ * MUST reply to only the first fragment.
+ */
+/*
+ * Send ICMP_UNREACH back to the source regarding msrc.
+ * mbuf *msrc is used as a template, but is NOT m_free()'d.
+ * It is reported as the bad ip packet. The header should
+ * be fully correct and in host byte order.
+ * ICMP fragmentation is illegal. All machines must accept 576 bytes in one
+ * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548
+ */
+
+#define ICMP_MAXDATALEN (IP_MSS-28)
+void
+icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+ const char *message)
+{
+ unsigned hlen, shlen, s_ip_len;
+ register struct ip *ip;
+ register struct icmp *icp;
+ register struct mbuf *m;
+
+ DEBUG_CALL("icmp_error");
+ DEBUG_ARG("msrc = %lx", (long )msrc);
+ DEBUG_ARG("msrc_len = %d", msrc->m_len);
+
+ if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error;
+
+ /* check msrc */
+ if(!msrc) goto end_error;
+ ip = mtod(msrc, struct ip *);
+#ifdef DEBUG
+ { char bufa[20], bufb[20];
+ strcpy(bufa, inet_ntoa(ip->ip_src));
+ strcpy(bufb, inet_ntoa(ip->ip_dst));
+ DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb));
+ }
+#endif
+ if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */
+
+ shlen=ip->ip_hl << 2;
+ s_ip_len=ip->ip_len;
+ if(ip->ip_p == IPPROTO_ICMP) {
+ icp = (struct icmp *)((char *)ip + shlen);
+ /*
+ * Assume any unknown ICMP type is an error. This isn't
+ * specified by the RFC, but think about it..
+ */
+ if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error;
+ }
+
+ /* make a copy */
+ m = m_get(msrc->slirp);
+ if (!m) {
+ goto end_error;
+ }
+
+ { int new_m_size;
+ new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN;
+ if(new_m_size>m->m_size) m_inc(m, new_m_size);
+ }
+ memcpy(m->m_data, msrc->m_data, msrc->m_len);
+ m->m_len = msrc->m_len; /* copy msrc to m */
+
+ /* make the header of the reply packet */
+ ip = mtod(m, struct ip *);
+ hlen= sizeof(struct ip ); /* no options in reply */
+
+ /* fill in icmp */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ icp = mtod(m, struct icmp *);
+
+ if(minsize) s_ip_len=shlen+ICMP_MINLEN; /* return header+8b only */
+ else if(s_ip_len>ICMP_MAXDATALEN) /* maximum size */
+ s_ip_len=ICMP_MAXDATALEN;
+
+ m->m_len=ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */
+
+ /* min. size = 8+sizeof(struct ip)+8 */
+
+ icp->icmp_type = type;
+ icp->icmp_code = code;
+ icp->icmp_id = 0;
+ icp->icmp_seq = 0;
+
+ memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */
+ HTONS(icp->icmp_ip.ip_len);
+ HTONS(icp->icmp_ip.ip_id);
+ HTONS(icp->icmp_ip.ip_off);
+
+#ifdef DEBUG
+ if(message) { /* DEBUG : append message to ICMP packet */
+ int message_len;
+ char *cpnt;
+ message_len=strlen(message);
+ if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN;
+ cpnt=(char *)m->m_data+m->m_len;
+ memcpy(cpnt, message, message_len);
+ m->m_len+=message_len;
+ }
+#endif
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, m->m_len);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ /* fill in ip */
+ ip->ip_hl = hlen >> 2;
+ ip->ip_len = m->m_len;
+
+ ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
+
+ ip->ip_ttl = MAXTTL;
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_dst = ip->ip_src; /* ip adresses */
+ ip->ip_src = m->slirp->vhost_addr;
+
+ (void ) ip_output((struct socket *)NULL, m);
+
+end_error:
+ return;
+}
+#undef ICMP_MAXDATALEN
+
+/*
+ * Reflect the ip packet back to the source
+ */
+void
+icmp_reflect(struct mbuf *m)
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ int hlen = ip->ip_hl << 2;
+ int optlen = hlen - sizeof(struct ip );
+ register struct icmp *icp;
+
+ /*
+ * Send an icmp packet back to the ip level,
+ * after supplying a checksum.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ /* fill in ip */
+ if (optlen > 0) {
+ /*
+ * Strip out original options by copying rest of first
+ * mbuf's data back, and adjust the IP length.
+ */
+ memmove((caddr_t)(ip + 1), (caddr_t)ip + hlen,
+ (unsigned )(m->m_len - hlen));
+ hlen -= optlen;
+ ip->ip_hl = hlen >> 2;
+ ip->ip_len -= optlen;
+ m->m_len -= optlen;
+ }
+
+ ip->ip_ttl = MAXTTL;
+ { /* swap */
+ struct in_addr icmp_dst;
+ icmp_dst = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = icmp_dst;
+ }
+
+ (void ) ip_output((struct socket *)NULL, m);
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_icmp.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_icmp.h
new file mode 100644
index 0000000..2692822
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_icmp.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
+ * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp
+ */
+
+#ifndef _NETINET_IP_ICMP_H_
+#define _NETINET_IP_ICMP_H_
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+typedef uint32_t n_time;
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp {
+ u_char icmp_type; /* type of message, see below */
+ u_char icmp_code; /* type sub code */
+ u_short icmp_cksum; /* ones complement cksum of struct */
+ union {
+ u_char ih_pptr; /* ICMP_PARAMPROB */
+ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
+ struct ih_idseq {
+ u_short icd_id;
+ u_short icd_seq;
+ } ih_idseq;
+ int ih_void;
+
+ /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
+ struct ih_pmtu {
+ u_short ipm_void;
+ u_short ipm_nextmtu;
+ } ih_pmtu;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+ union {
+ struct id_ts {
+ n_time its_otime;
+ n_time its_rtime;
+ n_time its_ttime;
+ } id_ts;
+ struct id_ip {
+ struct ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ uint32_t id_mask;
+ char id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+};
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define ICMP_MINLEN 8 /* abs minimum */
+#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
+#define ICMP_MASKLEN 12 /* address mask */
+#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
+#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
+#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
+#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
+#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_REDIRECT 5 /* shorter route, codes: */
+#define ICMP_REDIRECT_NET 0 /* for network */
+#define ICMP_REDIRECT_HOST 1 /* for host */
+#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
+#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
+#define ICMP_ECHO 8 /* echo service */
+#define ICMP_ROUTERADVERT 9 /* router advertisement */
+#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
+#define ICMP_TIMXCEED 11 /* time exceeded, code: */
+#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
+#define ICMP_PARAMPROB 12 /* ip header bad */
+#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
+#define ICMP_TSTAMP 13 /* timestamp request */
+#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
+#define ICMP_IREQ 15 /* information request */
+#define ICMP_IREQREPLY 16 /* information reply */
+#define ICMP_MASKREQ 17 /* address mask request */
+#define ICMP_MASKREPLY 18 /* address mask reply */
+
+#define ICMP_MAXTYPE 18
+
+#define ICMP_INFOTYPE(type) \
+ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
+ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+
+void icmp_input(struct mbuf *, int);
+void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+ const char *message);
+void icmp_reflect(struct mbuf *);
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_input.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_input.c
new file mode 100644
index 0000000..50ab951
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_input.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
+ * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp);
+static void ip_freef(Slirp *slirp, struct ipq *fp);
+static void ip_enq(register struct ipasfrag *p,
+ register struct ipasfrag *prev);
+static void ip_deq(register struct ipasfrag *p);
+
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void
+ip_init(Slirp *slirp)
+{
+ slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link;
+ udp_init(slirp);
+ tcp_init(slirp);
+}
+
+/*
+ * Ip input routine. Checksum and byte swap header. If fragmented
+ * try to reassemble. Process options. Pass to next level.
+ */
+void
+ip_input(struct mbuf *m)
+{
+ Slirp *slirp = m->slirp;
+ register struct ip *ip;
+ int hlen;
+
+ DEBUG_CALL("ip_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ if (m->m_len < sizeof (struct ip)) {
+ return;
+ }
+
+ ip = mtod(m, struct ip *);
+
+ if (ip->ip_v != IPVERSION) {
+ goto bad;
+ }
+
+ hlen = ip->ip_hl << 2;
+ if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */
+ goto bad; /* or packet too short */
+ }
+
+ /* keep ip header intact for ICMP reply
+ * ip->ip_sum = cksum(m, hlen);
+ * if (ip->ip_sum) {
+ */
+ if(cksum(m,hlen)) {
+ goto bad;
+ }
+
+ /*
+ * Convert fields to host representation.
+ */
+ NTOHS(ip->ip_len);
+ if (ip->ip_len < hlen) {
+ goto bad;
+ }
+ NTOHS(ip->ip_id);
+ NTOHS(ip->ip_off);
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IP header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (m->m_len < ip->ip_len) {
+ goto bad;
+ }
+
+ if (slirp->restricted) {
+ if ((ip->ip_dst.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p != IPPROTO_UDP)
+ goto bad;
+ } else {
+ uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
+ struct ex_list *ex_ptr;
+
+ if ((ip->ip_dst.s_addr & inv_mask) == inv_mask) {
+ goto bad;
+ }
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
+ if (ex_ptr->ex_addr.s_addr == ip->ip_dst.s_addr)
+ break;
+
+ if (!ex_ptr)
+ goto bad;
+ }
+ }
+
+ /* Should drop packet if mbuf too long? hmmm... */
+ if (m->m_len > ip->ip_len)
+ m_adj(m, ip->ip_len - m->m_len);
+
+ /* check ip_ttl for a correct ICMP reply */
+ if(ip->ip_ttl==0) {
+ icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
+ goto bad;
+ }
+
+ /*
+ * If offset or IP_MF are set, must reassemble.
+ * Otherwise, nothing need be done.
+ * (We could look in the reassembly queue to see
+ * if the packet was previously fragmented,
+ * but it's not worth the time; just let them time out.)
+ *
+ * XXX This should fail, don't fragment yet
+ */
+ if (ip->ip_off &~ IP_DF) {
+ register struct ipq *fp;
+ struct qlink *l;
+ /*
+ * Look for queue of fragments
+ * of this datagram.
+ */
+ for (l = slirp->ipq.ip_link.next; l != &slirp->ipq.ip_link;
+ l = l->next) {
+ fp = container_of(l, struct ipq, ip_link);
+ if (ip->ip_id == fp->ipq_id &&
+ ip->ip_src.s_addr == fp->ipq_src.s_addr &&
+ ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
+ ip->ip_p == fp->ipq_p)
+ goto found;
+ }
+ fp = NULL;
+ found:
+
+ /*
+ * Adjust ip_len to not reflect header,
+ * set ip_mff if more fragments are expected,
+ * convert offset of this to bytes.
+ */
+ ip->ip_len -= hlen;
+ if (ip->ip_off & IP_MF)
+ ip->ip_tos |= 1;
+ else
+ ip->ip_tos &= ~1;
+
+ ip->ip_off <<= 3;
+
+ /*
+ * If datagram marked as having more fragments
+ * or if this is not the first fragment,
+ * attempt reassembly; if it succeeds, proceed.
+ */
+ if (ip->ip_tos & 1 || ip->ip_off) {
+ ip = ip_reass(slirp, ip, fp);
+ if (ip == NULL)
+ return;
+ m = dtom(slirp, ip);
+ } else
+ if (fp)
+ ip_freef(slirp, fp);
+
+ } else
+ ip->ip_len -= hlen;
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ tcp_input(m, hlen, (struct socket *)NULL);
+ break;
+ case IPPROTO_UDP:
+ udp_input(m, hlen);
+ break;
+ case IPPROTO_ICMP:
+ icmp_input(m, hlen);
+ break;
+ default:
+ m_free(m);
+ }
+ return;
+bad:
+ m_freem(m);
+ return;
+}
+
+#define iptofrag(P) ((struct ipasfrag *)(((char*)(P)) - sizeof(struct qlink)))
+#define fragtoip(P) ((struct ip*)(((char*)(P)) + sizeof(struct qlink)))
+/*
+ * Take incoming datagram fragment and try to
+ * reassemble it into whole datagram. If a chain for
+ * reassembly of this datagram already exists, then it
+ * is given as fp; otherwise have to make a chain.
+ */
+static struct ip *
+ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)
+{
+ register struct mbuf *m = dtom(slirp, ip);
+ register struct ipasfrag *q;
+ int hlen = ip->ip_hl << 2;
+ int i, next;
+
+ DEBUG_CALL("ip_reass");
+ DEBUG_ARG("ip = %lx", (long)ip);
+ DEBUG_ARG("fp = %lx", (long)fp);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ /*
+ * Presence of header sizes in mbufs
+ * would confuse code below.
+ * Fragment m_data is concatenated.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ /*
+ * If first fragment to arrive, create a reassembly queue.
+ */
+ if (fp == NULL) {
+ struct mbuf *t = m_get(slirp);
+
+ if (t == NULL) {
+ goto dropfrag;
+ }
+ fp = mtod(t, struct ipq *);
+ insque(&fp->ip_link, &slirp->ipq.ip_link);
+ fp->ipq_ttl = IPFRAGTTL;
+ fp->ipq_p = ip->ip_p;
+ fp->ipq_id = ip->ip_id;
+ fp->frag_link.next = fp->frag_link.prev = &fp->frag_link;
+ fp->ipq_src = ip->ip_src;
+ fp->ipq_dst = ip->ip_dst;
+ q = (struct ipasfrag *)fp;
+ goto insert;
+ }
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link;
+ q = q->ipf_next)
+ if (q->ipf_off > ip->ip_off)
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if (q->ipf_prev != &fp->frag_link) {
+ struct ipasfrag *pq = q->ipf_prev;
+ i = pq->ipf_off + pq->ipf_len - ip->ip_off;
+ if (i > 0) {
+ if (i >= ip->ip_len)
+ goto dropfrag;
+ m_adj(dtom(slirp, ip), i);
+ ip->ip_off += i;
+ ip->ip_len -= i;
+ }
+ }
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct ipasfrag*)&fp->frag_link &&
+ ip->ip_off + ip->ip_len > q->ipf_off) {
+ i = (ip->ip_off + ip->ip_len) - q->ipf_off;
+ if (i < q->ipf_len) {
+ q->ipf_len -= i;
+ q->ipf_off += i;
+ m_adj(dtom(slirp, q), i);
+ break;
+ }
+ q = q->ipf_next;
+ m_freem(dtom(slirp, q->ipf_prev));
+ ip_deq(q->ipf_prev);
+ }
+
+insert:
+ /*
+ * Stick new segment in its place;
+ * check for complete reassembly.
+ */
+ ip_enq(iptofrag(ip), q->ipf_prev);
+ next = 0;
+ for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link;
+ q = q->ipf_next) {
+ if (q->ipf_off != next)
+ return NULL;
+ next += q->ipf_len;
+ }
+ if (((struct ipasfrag *)(q->ipf_prev))->ipf_tos & 1)
+ return NULL;
+
+ /*
+ * Reassembly is complete; concatenate fragments.
+ */
+ q = fp->frag_link.next;
+ m = dtom(slirp, q);
+
+ q = (struct ipasfrag *) q->ipf_next;
+ while (q != (struct ipasfrag*)&fp->frag_link) {
+ struct mbuf *t = dtom(slirp, q);
+ q = (struct ipasfrag *) q->ipf_next;
+ m_cat(m, t);
+ }
+
+ /*
+ * Create header for new ip packet by
+ * modifying header of first packet;
+ * dequeue and discard fragment reassembly header.
+ * Make header visible.
+ */
+ q = fp->frag_link.next;
+
+ /*
+ * If the fragments concatenated to an mbuf that's
+ * bigger than the total size of the fragment, then and
+ * m_ext buffer was alloced. But fp->ipq_next points to
+ * the old buffer (in the mbuf), so we must point ip
+ * into the new buffer.
+ */
+ if (m->m_flags & M_EXT) {
+ int delta = (char *)q - m->m_dat;
+ q = (struct ipasfrag *)(m->m_ext + delta);
+ }
+
+ ip = fragtoip(q);
+ ip->ip_len = next;
+ ip->ip_tos &= ~1;
+ ip->ip_src = fp->ipq_src;
+ ip->ip_dst = fp->ipq_dst;
+ remque(&fp->ip_link);
+ (void) m_free(dtom(slirp, fp));
+ m->m_len += (ip->ip_hl << 2);
+ m->m_data -= (ip->ip_hl << 2);
+
+ return ip;
+
+dropfrag:
+ m_freem(m);
+ return NULL;
+}
+
+/*
+ * Free a fragment reassembly header and all
+ * associated datagrams.
+ */
+static void
+ip_freef(Slirp *slirp, struct ipq *fp)
+{
+ register struct ipasfrag *q, *p;
+
+ for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) {
+ p = q->ipf_next;
+ ip_deq(q);
+ m_freem(dtom(slirp, q));
+ }
+ remque(&fp->ip_link);
+ (void) m_free(dtom(slirp, fp));
+}
+
+/*
+ * Put an ip fragment on a reassembly chain.
+ * Like insque, but pointers in middle of structure.
+ */
+static void
+ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev)
+{
+ DEBUG_CALL("ip_enq");
+ DEBUG_ARG("prev = %lx", (long)prev);
+ p->ipf_prev = prev;
+ p->ipf_next = prev->ipf_next;
+ ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p;
+ prev->ipf_next = p;
+}
+
+/*
+ * To ip_enq as remque is to insque.
+ */
+static void
+ip_deq(register struct ipasfrag *p)
+{
+ ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next;
+ ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+ip_slowtimo(Slirp *slirp)
+{
+ struct qlink *l;
+
+ DEBUG_CALL("ip_slowtimo");
+
+ l = slirp->ipq.ip_link.next;
+
+ if (l == NULL)
+ return;
+
+ while (l != &slirp->ipq.ip_link) {
+ struct ipq *fp = container_of(l, struct ipq, ip_link);
+ l = l->next;
+ if (--fp->ipq_ttl == 0) {
+ ip_freef(slirp, fp);
+ }
+ }
+}
+
+/*
+ * Do option processing on a datagram,
+ * possibly discarding it if bad options are encountered,
+ * or forwarding it if source-routed.
+ * Returns 1 if packet has been forwarded/freed,
+ * 0 if the packet should be processed further.
+ */
+
+#ifdef notdef
+
+int
+ip_dooptions(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register u_char *cp;
+ register struct ip_timestamp *ipt;
+ register struct in_ifaddr *ia;
+ int opt, optlen, cnt, off, code, type, forward = 0;
+ struct in_addr *sin, dst;
+typedef uint32_t n_time;
+ n_time ntime;
+
+ dst = ip->ip_dst;
+ cp = (u_char *)(ip + 1);
+ cnt = (ip->ip_hl << 2) - sizeof (struct ip);
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[IPOPT_OPTVAL];
+ if (opt == IPOPT_EOL)
+ break;
+ if (opt == IPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[IPOPT_OLEN];
+ if (optlen <= 0 || optlen > cnt) {
+ code = &cp[IPOPT_OLEN] - (u_char *)ip;
+ goto bad;
+ }
+ }
+ switch (opt) {
+
+ default:
+ break;
+
+ /*
+ * Source routing with record.
+ * Find interface with current destination address.
+ * If none on this machine then drop if strictly routed,
+ * or do nothing if loosely routed.
+ * Record interface address and bring up next address
+ * component. If strictly routed make sure next
+ * address is on directly accessible net.
+ */
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ ipaddr.sin_addr = ip->ip_dst;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithaddr((struct sockaddr *)&ipaddr);
+ if (ia == 0) {
+ if (opt == IPOPT_SSRR) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ /*
+ * Loose routing, and not at next destination
+ * yet; nothing to do except forward.
+ */
+ break;
+ }
+ off--; / * 0 origin * /
+ if (off > optlen - sizeof(struct in_addr)) {
+ /*
+ * End of source route. Should be for us.
+ */
+ save_rte(cp, ip->ip_src);
+ break;
+ }
+ /*
+ * locate outgoing interface
+ */
+ bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ if (opt == IPOPT_SSRR) {
+#define INA struct in_ifaddr *
+#define SA struct sockaddr *
+ if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
+ ia = (INA)ifa_ifwithnet((SA)&ipaddr);
+ } else
+ ia = ip_rtaddr(ipaddr.sin_addr);
+ if (ia == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ ip->ip_dst = ipaddr.sin_addr;
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ /*
+ * Let ip_intr's mcast routing check handle mcast pkts
+ */
+ forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
+ break;
+
+ case IPOPT_RR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ /*
+ * If no space remains, ignore.
+ */
+ off--; * 0 origin *
+ if (off > optlen - sizeof(struct in_addr))
+ break;
+ bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ /*
+ * locate outgoing interface; if we're the destination,
+ * use the incoming interface (should be same).
+ */
+ if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
+ (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_HOST;
+ goto bad;
+ }
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS:
+ code = cp - (u_char *)ip;
+ ipt = (struct ip_timestamp *)cp;
+ if (ipt->ipt_len < 5)
+ goto bad;
+ if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) {
+ if (++ipt->ipt_oflw == 0)
+ goto bad;
+ break;
+ }
+ sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
+ switch (ipt->ipt_flg) {
+
+ case IPOPT_TS_TSONLY:
+ break;
+
+ case IPOPT_TS_TSANDADDR:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ ipaddr.sin_addr = dst;
+ ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr,
+ m->m_pkthdr.rcvif);
+ if (ia == 0)
+ continue;
+ bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
+ (caddr_t)sin, sizeof(struct in_addr));
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS_PRESPEC:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
+ sizeof(struct in_addr));
+ if (ifa_ifwithaddr((SA)&ipaddr) == 0)
+ continue;
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ default:
+ goto bad;
+ }
+ ntime = iptime();
+ bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
+ sizeof(n_time));
+ ipt->ipt_ptr += sizeof(n_time);
+ }
+ }
+ if (forward) {
+ ip_forward(m, 1);
+ return (1);
+ }
+ return (0);
+bad:
+ icmp_error(m, type, code, 0, 0);
+
+ return (1);
+}
+
+#endif /* notdef */
+
+/*
+ * Strip out IP options, at higher
+ * level protocol in the kernel.
+ * Second argument is buffer to which options
+ * will be moved, and return value is their length.
+ * (XXX) should be deleted; last arg currently ignored.
+ */
+void
+ip_stripoptions(register struct mbuf *m, struct mbuf *mopt)
+{
+ register int i;
+ struct ip *ip = mtod(m, struct ip *);
+ register caddr_t opts;
+ int olen;
+
+ olen = (ip->ip_hl<<2) - sizeof (struct ip);
+ opts = (caddr_t)(ip + 1);
+ i = m->m_len - (sizeof (struct ip) + olen);
+ memcpy(opts, opts + olen, (unsigned)i);
+ m->m_len -= olen;
+
+ ip->ip_hl = sizeof(struct ip) >> 2;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_output.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_output.c
new file mode 100644
index 0000000..657c9af
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/ip_output.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94
+ * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+
+/* Number of packets queued before we start sending
+ * (to prevent allocing too many mbufs) */
+#define IF_THRESH 10
+
+/*
+ * IP output. The packet in mbuf chain m contains a skeletal IP
+ * header (with len, off, ttl, proto, tos, src, dst).
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+int
+ip_output(struct socket *so, struct mbuf *m0)
+{
+ Slirp *slirp = m0->slirp;
+ register struct ip *ip;
+ register struct mbuf *m = m0;
+ register int hlen = sizeof(struct ip );
+ int len, off, error = 0;
+
+ DEBUG_CALL("ip_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m0 = %lx", (long)m0);
+
+ ip = mtod(m, struct ip *);
+ /*
+ * Fill in IP header.
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_off &= IP_DF;
+ ip->ip_id = htons(slirp->ip_id++);
+ ip->ip_hl = hlen >> 2;
+
+ /*
+ * If small enough for interface, can just send directly.
+ */
+ if ((uint16_t)ip->ip_len <= IF_MTU) {
+ ip->ip_len = htons((uint16_t)ip->ip_len);
+ ip->ip_off = htons((uint16_t)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+
+ if_output(so, m);
+ goto done;
+ }
+
+ /*
+ * Too large for interface; fragment if possible.
+ * Must be able to put at least 8 bytes per fragment.
+ */
+ if (ip->ip_off & IP_DF) {
+ error = -1;
+ goto bad;
+ }
+
+ len = (IF_MTU - hlen) &~ 7; /* ip databytes per packet */
+ if (len < 8) {
+ error = -1;
+ goto bad;
+ }
+
+ {
+ int mhlen, firstlen = len;
+ struct mbuf **mnext = &m->m_nextpkt;
+
+ /*
+ * Loop through length of segment after first fragment,
+ * make new header and copy data of each part and link onto chain.
+ */
+ m0 = m;
+ mhlen = sizeof (struct ip);
+ for (off = hlen + len; off < (uint16_t)ip->ip_len; off += len) {
+ register struct ip *mhip;
+ m = m_get(slirp);
+ if (m == NULL) {
+ error = -1;
+ goto sendorfree;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ mhip = mtod(m, struct ip *);
+ *mhip = *ip;
+
+ m->m_len = mhlen;
+ mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
+ if (ip->ip_off & IP_MF)
+ mhip->ip_off |= IP_MF;
+ if (off + len >= (uint16_t)ip->ip_len)
+ len = (uint16_t)ip->ip_len - off;
+ else
+ mhip->ip_off |= IP_MF;
+ mhip->ip_len = htons((uint16_t)(len + mhlen));
+
+ if (m_copy(m, m0, off, len) < 0) {
+ error = -1;
+ goto sendorfree;
+ }
+
+ mhip->ip_off = htons((uint16_t)mhip->ip_off);
+ mhip->ip_sum = 0;
+ mhip->ip_sum = cksum(m, mhlen);
+ *mnext = m;
+ mnext = &m->m_nextpkt;
+ }
+ /*
+ * Update first fragment by trimming what's been copied out
+ * and updating header, then send each fragment (in order).
+ */
+ m = m0;
+ m_adj(m, hlen + firstlen - (uint16_t)ip->ip_len);
+ ip->ip_len = htons((uint16_t)m->m_len);
+ ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF));
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+sendorfree:
+ for (m = m0; m; m = m0) {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ if (error == 0)
+ if_output(so, m);
+ else
+ m_freem(m);
+ }
+ }
+
+done:
+ return (error);
+
+bad:
+ m_freem(m0);
+ goto done;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/libslirp.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/libslirp.h
new file mode 100644
index 0000000..574852b
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/libslirp.h
@@ -0,0 +1,56 @@
+#ifndef _LIBSLIRP_H
+#define _LIBSLIRP_H
+
+#ifdef CONFIG_SLIRP
+
+#include <netinet/in.h>
+
+struct Slirp;
+typedef struct Slirp Slirp;
+
+int get_dns_addr(struct in_addr *pdns_addr);
+
+Slirp *slirp_init(int restricted, struct in_addr vnetwork,
+ struct in_addr vnetmask, struct in_addr vhost,
+ const char *vhostname, const char *tftp_path,
+ const char *bootfile, struct in_addr vdhcp_start,
+ struct in_addr vnameserver, void *opaque);
+void slirp_cleanup(Slirp *slirp);
+
+void slirp_select_fill(Slirp *slirp, int *pnfds,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds);
+
+void slirp_select_poll(Slirp *slirp,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds,
+ int select_error);
+
+void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
+
+/* you must provide the following functions: */
+int slirp_can_output(void *opaque);
+void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len);
+
+int slirp_add_hostfwd(Slirp *slirp, int is_udp,
+ struct in_addr host_addr, int host_port,
+ struct in_addr guest_addr, int guest_port);
+int slirp_remove_hostfwd(Slirp *slirp, int is_udp,
+ struct in_addr host_addr, int host_port);
+int slirp_add_exec(Slirp *slirp, int do_pty, const void *args,
+ struct in_addr *guest_addr, int guest_port);
+
+void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr,
+ int guest_port, const uint8_t *buf, int size);
+size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
+ int guest_port);
+int slirp_get_time_ms(void);
+
+#else /* !CONFIG_SLIRP */
+
+static inline void slirp_select_fill(int *pnfds, fd_set *readfds,
+ fd_set *writefds, fd_set *xfds) { }
+
+static inline void slirp_select_poll(fd_set *readfds, fd_set *writefds,
+ fd_set *xfds, int select_error) { }
+#endif /* !CONFIG_SLIRP */
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/main.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/main.h
new file mode 100644
index 0000000..0dd8d81
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/main.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define TOWRITEMAX 512
+
+extern int slirp_socket;
+extern int slirp_socket_unit;
+extern int slirp_socket_port;
+extern uint32_t slirp_socket_addr;
+extern char *slirp_socket_passwd;
+extern int ctty_closed;
+
+/*
+ * Get the difference in 2 times from updtim()
+ * Allow for wraparound times, "just in case"
+ * x is the greater of the 2 (current time) and y is
+ * what it's being compared against.
+ */
+#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y)
+
+extern char *slirp_tty;
+extern char *exec_shell;
+extern u_int curtime;
+extern fd_set *global_readfds, *global_writefds, *global_xfds;
+extern struct in_addr loopback_addr;
+extern char *username;
+extern char *socket_path;
+extern int towrite_max;
+extern int ppp_exit;
+extern int tcp_keepintvl;
+
+#define PROTO_SLIP 0x1
+#ifdef USE_PPP
+#define PROTO_PPP 0x2
+#endif
+
+void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len);
+ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/mbuf.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/mbuf.c
new file mode 100644
index 0000000..6c115ee
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/mbuf.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/*
+ * mbuf's in SLiRP are much simpler than the real mbufs in
+ * FreeBSD. They are fixed size, determined by the MTU,
+ * so that one whole packet can fit. Mbuf's cannot be
+ * chained together. If there's more data than the mbuf
+ * could hold, an external malloced buffer is pointed to
+ * by m_ext (and the data pointers) and M_EXT is set in
+ * the flags
+ */
+
+#include "slirp.h"
+
+#define MBUF_THRESH 30
+
+/*
+ * Find a nice value for msize
+ * XXX if_maxlinkhdr already in mtu
+ */
+#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6)
+
+void
+m_init(Slirp *slirp)
+{
+ slirp->m_freelist.m_next = slirp->m_freelist.m_prev = &slirp->m_freelist;
+ slirp->m_usedlist.m_next = slirp->m_usedlist.m_prev = &slirp->m_usedlist;
+}
+
+/*
+ * Get an mbuf from the free list, if there are none
+ * malloc one
+ *
+ * Because fragmentation can occur if we alloc new mbufs and
+ * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
+ * which tells m_free to actually free() it
+ */
+struct mbuf *
+m_get(Slirp *slirp)
+{
+ register struct mbuf *m;
+ int flags = 0;
+
+ DEBUG_CALL("m_get");
+
+ if (slirp->m_freelist.m_next == &slirp->m_freelist) {
+ m = (struct mbuf *)malloc(SLIRP_MSIZE);
+ if (m == NULL) goto end_error;
+ slirp->mbuf_alloced++;
+ if (slirp->mbuf_alloced > MBUF_THRESH)
+ flags = M_DOFREE;
+ m->slirp = slirp;
+ } else {
+ m = slirp->m_freelist.m_next;
+ remque(m);
+ }
+
+ /* Insert it in the used list */
+ insque(m,&slirp->m_usedlist);
+ m->m_flags = (flags | M_USEDLIST);
+
+ /* Initialise it */
+ m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat);
+ m->m_data = m->m_dat;
+ m->m_len = 0;
+ m->m_nextpkt = NULL;
+ m->m_prevpkt = NULL;
+end_error:
+ DEBUG_ARG("m = %lx", (long )m);
+ return m;
+}
+
+void
+m_free(struct mbuf *m)
+{
+
+ DEBUG_CALL("m_free");
+ DEBUG_ARG("m = %lx", (long )m);
+
+ if(m) {
+ /* Remove from m_usedlist */
+ if (m->m_flags & M_USEDLIST)
+ remque(m);
+
+ /* If it's M_EXT, free() it */
+ if (m->m_flags & M_EXT)
+ free(m->m_ext);
+
+ /*
+ * Either free() it or put it on the free list
+ */
+ if (m->m_flags & M_DOFREE) {
+ m->slirp->mbuf_alloced--;
+ free(m);
+ } else if ((m->m_flags & M_FREELIST) == 0) {
+ insque(m,&m->slirp->m_freelist);
+ m->m_flags = M_FREELIST; /* Clobber other flags */
+ }
+ } /* if(m) */
+}
+
+/*
+ * Copy data from one mbuf to the end of
+ * the other.. if result is too big for one mbuf, malloc()
+ * an M_EXT data segment
+ */
+void
+m_cat(struct mbuf *m, struct mbuf *n)
+{
+ /*
+ * If there's no room, realloc
+ */
+ if (M_FREEROOM(m) < n->m_len)
+ m_inc(m,m->m_size+MINCSIZE);
+
+ memcpy(m->m_data+m->m_len, n->m_data, n->m_len);
+ m->m_len += n->m_len;
+
+ m_free(n);
+}
+
+
+/* make m size bytes large */
+void
+m_inc(struct mbuf *m, int size)
+{
+ int datasize;
+
+ /* some compiles throw up on gotos. This one we can fake. */
+ if(m->m_size>size) return;
+
+ if (m->m_flags & M_EXT) {
+ datasize = m->m_data - m->m_ext;
+ m->m_ext = (char *)realloc(m->m_ext,size);
+ m->m_data = m->m_ext + datasize;
+ } else {
+ char *dat;
+ datasize = m->m_data - m->m_dat;
+ dat = (char *)malloc(size);
+ memcpy(dat, m->m_dat, m->m_size);
+
+ m->m_ext = dat;
+ m->m_data = m->m_ext + datasize;
+ m->m_flags |= M_EXT;
+ }
+
+ m->m_size = size;
+
+}
+
+
+
+void
+m_adj(struct mbuf *m, int len)
+{
+ if (m == NULL)
+ return;
+ if (len >= 0) {
+ /* Trim from head */
+ m->m_data += len;
+ m->m_len -= len;
+ } else {
+ /* Trim from tail */
+ len = -len;
+ m->m_len -= len;
+ }
+}
+
+
+/*
+ * Copy len bytes from m, starting off bytes into n
+ */
+int
+m_copy(struct mbuf *n, struct mbuf *m, int off, int len)
+{
+ if (len > M_FREEROOM(n))
+ return -1;
+
+ memcpy((n->m_data + n->m_len), (m->m_data + off), len);
+ n->m_len += len;
+ return 0;
+}
+
+
+/*
+ * Given a pointer into an mbuf, return the mbuf
+ * XXX This is a kludge, I should eliminate the need for it
+ * Fortunately, it's not used often
+ */
+struct mbuf *
+dtom(Slirp *slirp, void *dat)
+{
+ struct mbuf *m;
+
+ DEBUG_CALL("dtom");
+ DEBUG_ARG("dat = %lx", (long )dat);
+
+ /* bug corrected for M_EXT buffers */
+ for (m = slirp->m_usedlist.m_next; m != &slirp->m_usedlist;
+ m = m->m_next) {
+ if (m->m_flags & M_EXT) {
+ if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) )
+ return m;
+ } else {
+ if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) )
+ return m;
+ }
+ }
+
+ DEBUG_ERROR((dfd, "dtom failed"));
+
+ return (struct mbuf *)0;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/mbuf.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/mbuf.h
new file mode 100644
index 0000000..97729e2
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/mbuf.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mbuf.h 8.3 (Berkeley) 1/21/94
+ * mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp
+ */
+
+#ifndef _MBUF_H_
+#define _MBUF_H_
+
+#define m_freem m_free
+
+
+#define MINCSIZE 4096 /* Amount to increase mbuf if too small */
+
+/*
+ * Macros for type conversion
+ * mtod(m,t) - convert mbuf pointer to data pointer of correct type
+ */
+#define mtod(m,t) ((t)(m)->m_data)
+
+/* XXX About mbufs for slirp:
+ * Only one mbuf is ever used in a chain, for each "cell" of data.
+ * m_nextpkt points to the next packet, if fragmented.
+ * If the data is too large, the M_EXT is used, and a larger block
+ * is alloced. Therefore, m_free[m] must check for M_EXT and if set
+ * free the m_ext. This is inefficient memory-wise, but who cares.
+ */
+
+/* XXX should union some of these! */
+/* header at beginning of each mbuf: */
+struct m_hdr {
+ struct mbuf *mh_next; /* Linked list of mbufs */
+ struct mbuf *mh_prev;
+ struct mbuf *mh_nextpkt; /* Next packet in queue/record */
+ struct mbuf *mh_prevpkt; /* Flags aren't used in the output queue */
+ int mh_flags; /* Misc flags */
+
+ int mh_size; /* Size of data */
+ struct socket *mh_so;
+
+ caddr_t mh_data; /* Location of data */
+ int mh_len; /* Amount of data in this mbuf */
+};
+
+/*
+ * How much room is in the mbuf, from m_data to the end of the mbuf
+ */
+#define M_ROOM(m) ((m->m_flags & M_EXT)? \
+ (((m)->m_ext + (m)->m_size) - (m)->m_data) \
+ : \
+ (((m)->m_dat + (m)->m_size) - (m)->m_data))
+
+/*
+ * How much free room there is
+ */
+#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len)
+#define M_TRAILINGSPACE M_FREEROOM
+
+struct mbuf {
+ struct m_hdr m_hdr;
+ Slirp *slirp;
+ union M_dat {
+ char m_dat_[1]; /* ANSI don't like 0 sized arrays */
+ char *m_ext_;
+ } M_dat;
+};
+
+#define m_next m_hdr.mh_next
+#define m_prev m_hdr.mh_prev
+#define m_nextpkt m_hdr.mh_nextpkt
+#define m_prevpkt m_hdr.mh_prevpkt
+#define m_flags m_hdr.mh_flags
+#define m_len m_hdr.mh_len
+#define m_data m_hdr.mh_data
+#define m_size m_hdr.mh_size
+#define m_dat M_dat.m_dat_
+#define m_ext M_dat.m_ext_
+#define m_so m_hdr.mh_so
+
+#define ifq_prev m_prev
+#define ifq_next m_next
+#define ifs_prev m_prevpkt
+#define ifs_next m_nextpkt
+#define ifq_so m_so
+
+#define M_EXT 0x01 /* m_ext points to more (malloced) data */
+#define M_FREELIST 0x02 /* mbuf is on free list */
+#define M_USEDLIST 0x04 /* XXX mbuf is on used list (for dtom()) */
+#define M_DOFREE 0x08 /* when m_free is called on the mbuf, free()
+ * it rather than putting it on the free list */
+
+void m_init(Slirp *);
+struct mbuf * m_get(Slirp *);
+void m_free(struct mbuf *);
+void m_cat(register struct mbuf *, register struct mbuf *);
+void m_inc(struct mbuf *, int);
+void m_adj(struct mbuf *, int);
+int m_copy(struct mbuf *, struct mbuf *, int, int);
+struct mbuf * dtom(Slirp *, void *);
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/misc.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/misc.c
new file mode 100644
index 0000000..7a79cdb
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/misc.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+
+#ifdef DEBUG
+int slirp_debug = DBG_CALL|DBG_MISC|DBG_ERROR;
+#endif
+
+struct quehead {
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+inline void
+insque(void *a, void *b)
+{
+ register struct quehead *element = (struct quehead *) a;
+ register struct quehead *head = (struct quehead *) b;
+ element->qh_link = head->qh_link;
+ head->qh_link = (struct quehead *)element;
+ element->qh_rlink = (struct quehead *)head;
+ ((struct quehead *)(element->qh_link))->qh_rlink
+ = (struct quehead *)element;
+}
+
+inline void
+remque(void *a)
+{
+ register struct quehead *element = (struct quehead *) a;
+ ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
+ ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
+ element->qh_rlink = NULL;
+}
+
+int add_exec(struct ex_list **ex_ptr, int do_pty, char *exec,
+ struct in_addr addr, int port)
+{
+ struct ex_list *tmp_ptr;
+
+ /* First, check if the port is "bound" */
+ for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {
+ if (port == tmp_ptr->ex_fport &&
+ addr.s_addr == tmp_ptr->ex_addr.s_addr)
+ return -1;
+ }
+
+ tmp_ptr = *ex_ptr;
+ *ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list));
+ (*ex_ptr)->ex_fport = port;
+ (*ex_ptr)->ex_addr = addr;
+ (*ex_ptr)->ex_pty = do_pty;
+ (*ex_ptr)->ex_exec = (do_pty == 3) ? exec : strdup(exec);
+ (*ex_ptr)->ex_next = tmp_ptr;
+ return 0;
+}
+
+#ifndef HAVE_STRERROR
+
+/*
+ * For systems with no strerror
+ */
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(error)
+ int error;
+{
+ if (error < sys_nerr)
+ return sys_errlist[error];
+ else
+ return "Unknown error.";
+}
+
+#endif
+
+int os_socket(int domain, int type, int protocol)
+{
+ return socket(domain, type, protocol);
+}
+
+uint32_t os_get_time_ms(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000 +
+ (ts.tv_nsec / 1000000);
+}
+
+
+#if 1
+
+int
+fork_exec(struct socket *so, const char *ex, int do_pty)
+{
+ /* not implemented */
+ return 0;
+}
+
+#else
+
+/*
+ * XXX This is ugly
+ * We create and bind a socket, then fork off to another
+ * process, which connects to this socket, after which we
+ * exec the wanted program. If something (strange) happens,
+ * the accept() call could block us forever.
+ *
+ * do_pty = 0 Fork/exec inetd style
+ * do_pty = 1 Fork/exec using slirp.telnetd
+ * do_ptr = 2 Fork/exec using pty
+ */
+int
+fork_exec(struct socket *so, const char *ex, int do_pty)
+{
+ int s;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int opt;
+ int master = -1;
+ const char *argv[256];
+ /* don't want to clobber the original */
+ char *bptr;
+ const char *curarg;
+ int c, i, ret;
+
+ DEBUG_CALL("fork_exec");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("ex = %lx", (long)ex);
+ DEBUG_ARG("do_pty = %lx", (long)do_pty);
+
+ if (do_pty == 2) {
+ return 0;
+ } else {
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if ((s = os_socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
+ bind(s, (struct sockaddr *)&addr, addrlen) < 0 ||
+ listen(s, 1) < 0) {
+ lprint("Error: inet socket: %s\n", strerror(errno));
+ closesocket(s);
+
+ return 0;
+ }
+ }
+
+ switch(fork()) {
+ case -1:
+ lprint("Error: fork failed: %s\n", strerror(errno));
+ close(s);
+ if (do_pty == 2)
+ close(master);
+ return 0;
+
+ case 0:
+ /* Set the DISPLAY */
+ if (do_pty == 2) {
+ (void) close(master);
+#ifdef TIOCSCTTY /* XXXXX */
+ (void) setsid();
+ ioctl(s, TIOCSCTTY, (char *)NULL);
+#endif
+ } else {
+ getsockname(s, (struct sockaddr *)&addr, &addrlen);
+ close(s);
+ /*
+ * Connect to the socket
+ * XXX If any of these fail, we're in trouble!
+ */
+ s = os_socket(AF_INET, SOCK_STREAM, 0);
+ addr.sin_addr = loopback_addr;
+ do {
+ ret = connect(s, (struct sockaddr *)&addr, addrlen);
+ } while (ret < 0 && errno == EINTR);
+ }
+
+ dup2(s, 0);
+ dup2(s, 1);
+ dup2(s, 2);
+ for (s = getdtablesize() - 1; s >= 3; s--)
+ close(s);
+
+ i = 0;
+ bptr = qemu_strdup(ex); /* No need to free() this */
+ if (do_pty == 1) {
+ /* Setup "slirp.telnetd -x" */
+ argv[i++] = "slirp.telnetd";
+ argv[i++] = "-x";
+ argv[i++] = bptr;
+ } else
+ do {
+ /* Change the string into argv[] */
+ curarg = bptr;
+ while (*bptr != ' ' && *bptr != (char)0)
+ bptr++;
+ c = *bptr;
+ *bptr++ = (char)0;
+ argv[i++] = strdup(curarg);
+ } while (c);
+
+ argv[i] = NULL;
+ execvp(argv[0], (char **)argv);
+
+ /* Ooops, failed, let's tell the user why */
+ fprintf(stderr, "Error: execvp of %s failed: %s\n",
+ argv[0], strerror(errno));
+ close(0); close(1); close(2); /* XXX */
+ exit(1);
+
+ default:
+ if (do_pty == 2) {
+ close(s);
+ so->s = master;
+ } else {
+ /*
+ * XXX this could block us...
+ * XXX Should set a timer here, and if accept() doesn't
+ * return after X seconds, declare it a failure
+ * The only reason this will block forever is if socket()
+ * of connect() fail in the child process
+ */
+ do {
+ so->s = accept(s, (struct sockaddr *)&addr, &addrlen);
+ } while (so->s < 0 && errno == EINTR);
+ closesocket(s);
+ opt = 1;
+ setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+ }
+ fd_nonblock(so->s);
+
+ /* Append the telnet options now */
+ if (so->so_m != NULL && do_pty == 1) {
+ sbappend(so, so->so_m);
+ so->so_m = NULL;
+ }
+
+ return 1;
+ }
+}
+#endif
+
+#ifndef HAVE_STRDUP
+char *
+strdup(str)
+ const char *str;
+{
+ char *bptr;
+
+ bptr = (char *)malloc(strlen(str)+1);
+ strcpy(bptr, str);
+
+ return bptr;
+}
+#endif
+
+void lprint(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+}
+
+/*
+ * Set fd blocking and non-blocking
+ */
+
+void
+fd_nonblock(int fd)
+{
+#ifdef FIONBIO
+#ifdef _WIN32
+ unsigned long opt = 1;
+#else
+ int opt = 1;
+#endif
+
+ ioctlsocket(fd, FIONBIO, &opt);
+#else
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+#endif
+}
+
+void
+fd_block(int fd)
+{
+#ifdef FIONBIO
+#ifdef _WIN32
+ unsigned long opt = 0;
+#else
+ int opt = 0;
+#endif
+
+ ioctlsocket(fd, FIONBIO, &opt);
+#else
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt &= ~O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+#endif
+}
+
+#if 0
+void slirp_connection_info(Slirp *slirp, Monitor *mon)
+{
+ const char * const tcpstates[] = {
+ [TCPS_CLOSED] = "CLOSED",
+ [TCPS_LISTEN] = "LISTEN",
+ [TCPS_SYN_SENT] = "SYN_SENT",
+ [TCPS_SYN_RECEIVED] = "SYN_RCVD",
+ [TCPS_ESTABLISHED] = "ESTABLISHED",
+ [TCPS_CLOSE_WAIT] = "CLOSE_WAIT",
+ [TCPS_FIN_WAIT_1] = "FIN_WAIT_1",
+ [TCPS_CLOSING] = "CLOSING",
+ [TCPS_LAST_ACK] = "LAST_ACK",
+ [TCPS_FIN_WAIT_2] = "FIN_WAIT_2",
+ [TCPS_TIME_WAIT] = "TIME_WAIT",
+ };
+ struct in_addr dst_addr;
+ struct sockaddr_in src;
+ socklen_t src_len;
+ uint16_t dst_port;
+ struct socket *so;
+ const char *state;
+ char buf[20];
+ int n;
+
+ monitor_printf(mon, " Protocol[State] FD Source Address Port "
+ "Dest. Address Port RecvQ SendQ\n");
+
+ for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
+ if (so->so_state & SS_HOSTFWD) {
+ state = "HOST_FORWARD";
+ } else if (so->so_tcpcb) {
+ state = tcpstates[so->so_tcpcb->t_state];
+ } else {
+ state = "NONE";
+ }
+ if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) {
+ src_len = sizeof(src);
+ getsockname(so->s, (struct sockaddr *)&src, &src_len);
+ dst_addr = so->so_laddr;
+ dst_port = so->so_lport;
+ } else {
+ src.sin_addr = so->so_laddr;
+ src.sin_port = so->so_lport;
+ dst_addr = so->so_faddr;
+ dst_port = so->so_fport;
+ }
+ n = snprintf(buf, sizeof(buf), " TCP[%s]", state);
+ memset(&buf[n], ' ', 19 - n);
+ buf[19] = 0;
+ monitor_printf(mon, "%s %3d %15s %5d ", buf, so->s,
+ src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
+ ntohs(src.sin_port));
+ monitor_printf(mon, "%15s %5d %5d %5d\n",
+ inet_ntoa(dst_addr), ntohs(dst_port),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
+
+ for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) {
+ if (so->so_state & SS_HOSTFWD) {
+ n = snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]");
+ src_len = sizeof(src);
+ getsockname(so->s, (struct sockaddr *)&src, &src_len);
+ dst_addr = so->so_laddr;
+ dst_port = so->so_lport;
+ } else {
+ n = snprintf(buf, sizeof(buf), " UDP[%d sec]",
+ (so->so_expire - curtime) / 1000);
+ src.sin_addr = so->so_laddr;
+ src.sin_port = so->so_lport;
+ dst_addr = so->so_faddr;
+ dst_port = so->so_fport;
+ }
+ memset(&buf[n], ' ', 19 - n);
+ buf[19] = 0;
+ monitor_printf(mon, "%s %3d %15s %5d ", buf, so->s,
+ src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
+ ntohs(src.sin_port));
+ monitor_printf(mon, "%15s %5d %5d %5d\n",
+ inet_ntoa(dst_addr), ntohs(dst_port),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
+}
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/misc.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/misc.h
new file mode 100644
index 0000000..5c307c9
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/misc.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _MISC_H_
+#define _MISC_H_
+
+struct ex_list {
+ int ex_pty; /* Do we want a pty? */
+ struct in_addr ex_addr; /* Server address */
+ int ex_fport; /* Port to telnet to */
+ const char *ex_exec; /* Command line of what to exec */
+ struct ex_list *ex_next;
+};
+
+#ifndef HAVE_STRDUP
+char *strdup(const char *);
+#endif
+
+void do_wait(int);
+
+#define EMU_NONE 0x0
+
+/* TCP emulations */
+#define EMU_CTL 0x1
+#define EMU_FTP 0x2
+#define EMU_KSH 0x3
+#define EMU_IRC 0x4
+#define EMU_REALAUDIO 0x5
+#define EMU_RLOGIN 0x6
+#define EMU_IDENT 0x7
+#define EMU_RSH 0x8
+
+#define EMU_NOCONNECT 0x10 /* Don't connect */
+
+struct tos_t {
+ uint16_t lport;
+ uint16_t fport;
+ uint8_t tos;
+ uint8_t emu;
+};
+
+struct emu_t {
+ uint16_t lport;
+ uint16_t fport;
+ uint8_t tos;
+ uint8_t emu;
+ struct emu_t *next;
+};
+
+extern int x_port, x_server, x_display;
+
+int show_x(char *, struct socket *);
+void redir_x(uint32_t, int, int, int);
+void slirp_insque(void *, void *);
+void slirp_remque(void *);
+int add_exec(struct ex_list **, int, char *, struct in_addr, int);
+int slirp_openpty(int *, int *);
+int fork_exec(struct socket *so, const char *ex, int do_pty);
+void snooze_hup(int);
+void snooze(void);
+void relay(int);
+void add_emu(char *);
+void fd_nonblock(int);
+void fd_block(int);
+int rsh_exec(struct socket *, struct socket *, char *, char *, char *);
+int os_socket(int domain, int type, int protocol);
+uint32_t os_get_time_ms(void);
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/sbuf.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/sbuf.c
new file mode 100644
index 0000000..ac20f21
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/sbuf.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+
+static void sbappendsb(struct sbuf *sb, struct mbuf *m);
+
+void
+sbfree(struct sbuf *sb)
+{
+ free(sb->sb_data);
+}
+
+void
+sbdrop(struct sbuf *sb, int num)
+{
+ /*
+ * We can only drop how much we have
+ * This should never succeed
+ */
+ if(num > sb->sb_cc)
+ num = sb->sb_cc;
+ sb->sb_cc -= num;
+ sb->sb_rptr += num;
+ if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_rptr -= sb->sb_datalen;
+
+}
+
+void
+sbreserve(struct sbuf *sb, int size)
+{
+ if (sb->sb_data) {
+ /* Already alloced, realloc if necessary */
+ if (sb->sb_datalen != size) {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+ } else {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+}
+
+/*
+ * Try and write() to the socket, whatever doesn't get written
+ * append to the buffer... for a host with a fast net connection,
+ * this prevents an unnecessary copy of the data
+ * (the socket is non-blocking, so we won't hang)
+ */
+void
+sbappend(struct socket *so, struct mbuf *m)
+{
+ int ret = 0;
+
+ DEBUG_CALL("sbappend");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m->m_len = %d", m->m_len);
+
+ /* Shouldn't happen, but... e.g. foreign host closes connection */
+ if (m->m_len <= 0) {
+ m_free(m);
+ return;
+ }
+
+ /*
+ * If there is urgent data, call sosendoob
+ * if not all was sent, sowrite will take care of the rest
+ * (The rest of this function is just an optimisation)
+ */
+ if (so->so_urgc) {
+ sbappendsb(&so->so_rcv, m);
+ m_free(m);
+ sosendoob(so);
+ return;
+ }
+
+ /*
+ * We only write if there's nothing in the buffer,
+ * ottherwise it'll arrive out of order, and hence corrupt
+ */
+ if (!so->so_rcv.sb_cc)
+ ret = slirp_send(so, m->m_data, m->m_len, 0);
+
+ if (ret <= 0) {
+ /*
+ * Nothing was written
+ * It's possible that the socket has closed, but
+ * we don't need to check because if it has closed,
+ * it will be detected in the normal way by soread()
+ */
+ sbappendsb(&so->so_rcv, m);
+ } else if (ret != m->m_len) {
+ /*
+ * Something was written, but not everything..
+ * sbappendsb the rest
+ */
+ m->m_len -= ret;
+ m->m_data += ret;
+ sbappendsb(&so->so_rcv, m);
+ } /* else */
+ /* Whatever happened, we free the mbuf */
+ m_free(m);
+}
+
+/*
+ * Copy the data from m into sb
+ * The caller is responsible to make sure there's enough room
+ */
+static void
+sbappendsb(struct sbuf *sb, struct mbuf *m)
+{
+ int len, n, nn;
+
+ len = m->m_len;
+
+ if (sb->sb_wptr < sb->sb_rptr) {
+ n = sb->sb_rptr - sb->sb_wptr;
+ if (n > len) n = len;
+ memcpy(sb->sb_wptr, m->m_data, n);
+ } else {
+ /* Do the right edge first */
+ n = sb->sb_data + sb->sb_datalen - sb->sb_wptr;
+ if (n > len) n = len;
+ memcpy(sb->sb_wptr, m->m_data, n);
+ len -= n;
+ if (len) {
+ /* Now the left edge */
+ nn = sb->sb_rptr - sb->sb_data;
+ if (nn > len) nn = len;
+ memcpy(sb->sb_data,m->m_data+n,nn);
+ n += nn;
+ }
+ }
+
+ sb->sb_cc += n;
+ sb->sb_wptr += n;
+ if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_wptr -= sb->sb_datalen;
+}
+
+/*
+ * Copy data from sbuf to a normal, straight buffer
+ * Don't update the sbuf rptr, this will be
+ * done in sbdrop when the data is acked
+ */
+void
+sbcopy(struct sbuf *sb, int off, int len, char *to)
+{
+ char *from;
+
+ from = sb->sb_rptr + off;
+ if (from >= sb->sb_data + sb->sb_datalen)
+ from -= sb->sb_datalen;
+
+ if (from < sb->sb_wptr) {
+ if (len > sb->sb_cc) len = sb->sb_cc;
+ memcpy(to,from,len);
+ } else {
+ /* re-use off */
+ off = (sb->sb_data + sb->sb_datalen) - from;
+ if (off > len) off = len;
+ memcpy(to,from,off);
+ len -= off;
+ if (len)
+ memcpy(to+off,sb->sb_data,len);
+ }
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/sbuf.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/sbuf.h
new file mode 100644
index 0000000..4f22e7c
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/sbuf.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _SBUF_H_
+#define _SBUF_H_
+
+#define sbflush(sb) sbdrop((sb),(sb)->sb_cc)
+#define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc)
+
+struct sbuf {
+ u_int sb_cc; /* actual chars in buffer */
+ u_int sb_datalen; /* Length of data */
+ char *sb_wptr; /* write pointer. points to where the next
+ * bytes should be written in the sbuf */
+ char *sb_rptr; /* read pointer. points to where the next
+ * byte should be read from the sbuf */
+ char *sb_data; /* Actual data */
+};
+
+void sbfree(struct sbuf *);
+void sbdrop(struct sbuf *, int);
+void sbreserve(struct sbuf *, int);
+void sbappend(struct socket *, struct mbuf *);
+void sbcopy(struct sbuf *, int, int, char *);
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp.c
new file mode 100644
index 0000000..db8c117
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp.c
@@ -0,0 +1,828 @@
+/*
+ * libslirp glue
+ *
+ * Copyright (c) 2004-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "slirp.h"
+
+/* host loopback address */
+struct in_addr loopback_addr;
+
+/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */
+static const uint8_t special_ethaddr[6] = {
+ 0x52, 0x55, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t zero_ethaddr[6] = { 0, 0, 0, 0, 0, 0 };
+
+/* XXX: suppress those select globals */
+fd_set *global_readfds, *global_writefds, *global_xfds;
+
+u_int curtime;
+static u_int time_fasttimo, last_slowtimo;
+static int do_slowtimo;
+
+static struct in_addr dns_addr;
+static u_int dns_addr_time;
+
+#ifdef _WIN32
+
+int get_dns_addr(struct in_addr *pdns_addr)
+{
+ FIXED_INFO *FixedInfo=NULL;
+ ULONG BufLen;
+ DWORD ret;
+ IP_ADDR_STRING *pIPAddr;
+ struct in_addr tmp_addr;
+
+ if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < 1000) {
+ *pdns_addr = dns_addr;
+ return 0;
+ }
+
+ FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
+ BufLen = sizeof(FIXED_INFO);
+
+ if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ FixedInfo = GlobalAlloc(GPTR, BufLen);
+ }
+
+ if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {
+ printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret );
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ return -1;
+ }
+
+ pIPAddr = &(FixedInfo->DnsServerList);
+ inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
+ *pdns_addr = tmp_addr;
+ dns_addr = tmp_addr;
+ dns_addr_time = curtime;
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ return 0;
+}
+
+static void winsock_cleanup(void)
+{
+ WSACleanup();
+}
+
+#else
+
+static struct stat dns_addr_stat;
+
+int get_dns_addr(struct in_addr *pdns_addr)
+{
+ char buff[512];
+ char buff2[257];
+ FILE *f;
+ int found = 0;
+ struct in_addr tmp_addr;
+
+ if (dns_addr.s_addr != 0) {
+ struct stat old_stat;
+ if ((curtime - dns_addr_time) < 1000) {
+ *pdns_addr = dns_addr;
+ return 0;
+ }
+ old_stat = dns_addr_stat;
+ if (stat("/etc/resolv.conf", &dns_addr_stat) != 0)
+ return -1;
+ if ((dns_addr_stat.st_dev == old_stat.st_dev)
+ && (dns_addr_stat.st_ino == old_stat.st_ino)
+ && (dns_addr_stat.st_size == old_stat.st_size)
+ && (dns_addr_stat.st_mtime == old_stat.st_mtime)) {
+ *pdns_addr = dns_addr;
+ return 0;
+ }
+ }
+
+ f = fopen("/etc/resolv.conf", "r");
+ if (!f)
+ return -1;
+
+#ifdef DEBUG
+ lprint("IP address of your DNS(s): ");
+#endif
+ while (fgets(buff, 512, f) != NULL) {
+ if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
+ if (!inet_aton(buff2, &tmp_addr))
+ continue;
+ /* If it's the first one, set it to dns_addr */
+ if (!found) {
+ *pdns_addr = tmp_addr;
+ dns_addr = tmp_addr;
+ dns_addr_time = curtime;
+ }
+#ifdef DEBUG
+ else
+ lprint(", ");
+#endif
+ if (++found > 3) {
+#ifdef DEBUG
+ lprint("(more)");
+#endif
+ break;
+ }
+#ifdef DEBUG
+ else
+ lprint("%s", inet_ntoa(tmp_addr));
+#endif
+ }
+ }
+ fclose(f);
+ if (!found)
+ return -1;
+ return 0;
+}
+
+#endif
+
+static void slirp_init_once(void)
+{
+ static int initialized;
+#ifdef _WIN32
+ WSADATA Data;
+#endif
+
+ if (initialized) {
+ return;
+ }
+ initialized = 1;
+
+#ifdef _WIN32
+ WSAStartup(MAKEWORD(2,0), &Data);
+ atexit(winsock_cleanup);
+#endif
+
+ loopback_addr.s_addr = htonl(INADDR_LOOPBACK);
+}
+
+Slirp *slirp_init(int restricted, struct in_addr vnetwork,
+ struct in_addr vnetmask, struct in_addr vhost,
+ const char *vhostname, const char *tftp_path,
+ const char *bootfile, struct in_addr vdhcp_start,
+ struct in_addr vnameserver, void *opaque)
+{
+ Slirp *slirp = mallocz(sizeof(Slirp));
+
+ slirp_init_once();
+
+ slirp->restricted = restricted;
+
+ if_init(slirp);
+ ip_init(slirp);
+
+ /* Initialise mbufs *after* setting the MTU */
+ m_init(slirp);
+
+ slirp->vnetwork_addr = vnetwork;
+ slirp->vnetwork_mask = vnetmask;
+ slirp->vhost_addr = vhost;
+ if (vhostname) {
+ pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
+ vhostname);
+ }
+ if (tftp_path) {
+ slirp->tftp_prefix = strdup(tftp_path);
+ }
+ if (bootfile) {
+ slirp->bootp_filename = strdup(bootfile);
+ }
+ slirp->vdhcp_startaddr = vdhcp_start;
+ slirp->vnameserver_addr = vnameserver;
+
+ slirp->opaque = opaque;
+
+ return slirp;
+}
+
+void slirp_cleanup(Slirp *slirp)
+{
+ free(slirp->tftp_prefix);
+ free(slirp->bootp_filename);
+ free(slirp);
+}
+
+#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
+
+void slirp_select_fill(Slirp *slirp, int *pnfds,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds)
+{
+ struct socket *so, *so_next;
+ int nfds;
+
+ /* fail safe */
+ global_readfds = NULL;
+ global_writefds = NULL;
+ global_xfds = NULL;
+
+ nfds = *pnfds;
+ /*
+ * First, TCP sockets
+ */
+ do_slowtimo = 0;
+
+ {
+ /*
+ * *_slowtimo needs calling if there are IP fragments
+ * in the fragment queue, or there are TCP connections active
+ */
+ do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) ||
+ (&slirp->ipq.ip_link != slirp->ipq.ip_link.next));
+
+ for (so = slirp->tcb.so_next; so != &slirp->tcb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if we need a tcp_fasttimo
+ */
+ if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
+ time_fasttimo = curtime; /* Flag when we want a fasttimo */
+
+ /*
+ * NOFDREF can include still connecting to local-host,
+ * newly socreated() sockets etc. Don't want to select these.
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ continue;
+
+ /*
+ * Set for reading sockets which are accepting
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ continue;
+ }
+
+ /*
+ * Set for writing sockets which are connecting
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ FD_SET(so->s, writefds);
+ UPD_NFDS(so->s);
+ continue;
+ }
+
+ /*
+ * Set for writing if we are connected, can send more, and
+ * we have something to send
+ */
+ if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
+ FD_SET(so->s, writefds);
+ UPD_NFDS(so->s);
+ }
+
+ /*
+ * Set for reading (and urgent data) if we are connected, can
+ * receive more, and we have room for it XXX /2 ?
+ */
+ if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
+ FD_SET(so->s, readfds);
+ FD_SET(so->s, xfds);
+ UPD_NFDS(so->s);
+ }
+ }
+
+ /*
+ * UDP sockets
+ */
+ for (so = slirp->udb.so_next; so != &slirp->udb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if it's timed out
+ */
+ if (so->so_expire) {
+ if (so->so_expire <= curtime) {
+ udp_detach(so);
+ continue;
+ } else
+ do_slowtimo = 1; /* Let socket expire */
+ }
+
+ /*
+ * When UDP packets are received from over the
+ * link, they're sendto()'d straight away, so
+ * no need for setting for writing
+ * Limit the number of packets queued by this session
+ * to 4. Note that even though we try and limit this
+ * to 4 packets, the session could have more queued
+ * if the packets needed to be fragmented
+ * (XXX <= 4 ?)
+ */
+ if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ }
+ }
+ }
+
+ *pnfds = nfds;
+}
+
+void slirp_select_poll(Slirp *slirp,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds,
+ int select_error)
+{
+ struct socket *so, *so_next;
+ int ret;
+
+ global_readfds = readfds;
+ global_writefds = writefds;
+ global_xfds = xfds;
+
+ curtime = os_get_time_ms();
+
+ {
+ /*
+ * See if anything has timed out
+ */
+ if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
+ tcp_fasttimo(slirp);
+ time_fasttimo = 0;
+ }
+ if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
+ ip_slowtimo(slirp);
+ tcp_slowtimo(slirp);
+ last_slowtimo = curtime;
+ }
+
+ /*
+ * Check sockets
+ */
+ if (!select_error) {
+ /*
+ * Check TCP sockets
+ */
+ for (so = slirp->tcb.so_next; so != &slirp->tcb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * FD_ISSET is meaningless on these sockets
+ * (and they can crash the program)
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ continue;
+
+ /*
+ * Check for URG data
+ * This will soread as well, so no need to
+ * test for readfds below if this succeeds
+ */
+ if (FD_ISSET(so->s, xfds))
+ sorecvoob(so);
+ /*
+ * Check sockets for reading
+ */
+ else if (FD_ISSET(so->s, readfds)) {
+ /*
+ * Check for incoming connections
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ tcp_connect(so);
+ continue;
+ } /* else */
+ ret = soread(so);
+
+ /* Output it if we read something */
+ if (ret > 0)
+ tcp_output(sototcpcb(so));
+ }
+
+ /*
+ * Check sockets for writing
+ */
+ if (FD_ISSET(so->s, writefds)) {
+ /*
+ * Check for non-blocking, still-connecting sockets
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ /* Connected */
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ ret = send(so->s, (const void *) &ret, 0, 0);
+ if (ret < 0) {
+ /* XXXXX Must fix, zero bytes is a NOP */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue;
+
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
+ }
+ /* else so->so_state &= ~SS_ISFCONNECTING; */
+
+ /*
+ * Continue tcp_input
+ */
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
+ /* continue; */
+ } else
+ ret = sowrite(so);
+ /*
+ * XXXXX If we wrote something (a lot), there
+ * could be a need for a window update.
+ * In the worst case, the remote will send
+ * a window probe to get things going again
+ */
+ }
+
+ /*
+ * Probe a still-connecting, non-blocking socket
+ * to check if it's still alive
+ */
+#ifdef PROBE_CONN
+ if (so->so_state & SS_ISFCONNECTING) {
+ ret = recv(so->s, (char *)&ret, 0,0);
+
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue; /* Still connecting, continue */
+
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
+
+ /* tcp_input will take care of it */
+ } else {
+ ret = send(so->s, &ret, 0,0);
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue;
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
+ } else
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ }
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
+ } /* SS_ISFCONNECTING */
+#endif
+ }
+
+ /*
+ * Now UDP sockets.
+ * Incoming packets are sent straight away, they're not buffered.
+ * Incoming UDP data isn't buffered either.
+ */
+ for (so = slirp->udb.so_next; so != &slirp->udb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ if (so->s != -1 && FD_ISSET(so->s, readfds)) {
+ sorecvfrom(so);
+ }
+ }
+ }
+
+ /*
+ * See if we can start outputting
+ */
+ if (slirp->if_queued) {
+ if_start(slirp);
+ }
+ }
+
+ /* clear global file descriptor sets.
+ * these reside on the stack in vl.c
+ * so they're unusable if we're not in
+ * slirp_select_fill or slirp_select_poll.
+ */
+ global_readfds = NULL;
+ global_writefds = NULL;
+ global_xfds = NULL;
+}
+
+#define ETH_ALEN 6
+#define ETH_HLEN 14
+
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+
+struct ethhdr
+{
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+
+struct arphdr
+{
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ uint32_t ar_sip; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ uint32_t ar_tip ; /* target IP address */
+} __attribute__((packed));
+
+static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
+{
+ struct ethhdr *eh = (struct ethhdr *)pkt;
+ struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
+ uint8_t arp_reply[max(ETH_HLEN + sizeof(struct arphdr), 64)];
+ struct ethhdr *reh = (struct ethhdr *)arp_reply;
+ struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
+ int ar_op;
+ struct ex_list *ex_ptr;
+
+ ar_op = ntohs(ah->ar_op);
+ switch(ar_op) {
+ case ARPOP_REQUEST:
+ if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ if (ah->ar_tip == slirp->vnameserver_addr.s_addr ||
+ ah->ar_tip == slirp->vhost_addr.s_addr)
+ goto arp_ok;
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_addr.s_addr == ah->ar_tip)
+ goto arp_ok;
+ }
+ return;
+ arp_ok:
+ memset(arp_reply, 0, sizeof(arp_reply));
+ /* XXX: make an ARP request to have the client address */
+ memcpy(slirp->client_ethaddr, eh->h_source, ETH_ALEN);
+
+ /* ARP request for alias/dns mac address */
+ memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
+ memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
+ memcpy(&reh->h_source[2], &ah->ar_tip, 4);
+ reh->h_proto = htons(ETH_P_ARP);
+
+ rah->ar_hrd = htons(1);
+ rah->ar_pro = htons(ETH_P_IP);
+ rah->ar_hln = ETH_ALEN;
+ rah->ar_pln = 4;
+ rah->ar_op = htons(ARPOP_REPLY);
+ memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
+ rah->ar_sip = ah->ar_tip;
+ memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
+ rah->ar_tip = ah->ar_sip;
+ slirp_output(slirp->opaque, arp_reply, sizeof(arp_reply));
+ }
+ break;
+ case ARPOP_REPLY:
+ /* reply to request of client mac address ? */
+ if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN) &&
+ ah->ar_sip == slirp->client_ipaddr.s_addr) {
+ memcpy(slirp->client_ethaddr, ah->ar_sha, ETH_ALEN);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
+{
+ struct mbuf *m;
+ int proto;
+
+ if (pkt_len < ETH_HLEN)
+ return;
+
+ proto = ntohs(*(uint16_t *)(pkt + 12));
+ switch(proto) {
+ case ETH_P_ARP:
+ arp_input(slirp, pkt, pkt_len);
+ break;
+ case ETH_P_IP:
+ m = m_get(slirp);
+ if (!m)
+ return;
+ /* Note: we add to align the IP header */
+ if (M_FREEROOM(m) < pkt_len + 2) {
+ m_inc(m, pkt_len + 2);
+ }
+ m->m_len = pkt_len + 2;
+ memcpy(m->m_data + 2, pkt, pkt_len);
+
+ m->m_data += 2 + ETH_HLEN;
+ m->m_len -= 2 + ETH_HLEN;
+
+ ip_input(m);
+ break;
+ default:
+ break;
+ }
+}
+
+/* output the IP packet to the ethernet device */
+void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len)
+{
+ uint8_t buf[1600];
+ struct ethhdr *eh = (struct ethhdr *)buf;
+
+ if (ip_data_len + ETH_HLEN > sizeof(buf))
+ return;
+
+ if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN)) {
+ uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)];
+ struct ethhdr *reh = (struct ethhdr *)arp_req;
+ struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN);
+ const struct ip *iph = (const struct ip *)ip_data;
+
+ /* If the client addr is not known, there is no point in
+ sending the packet to it. Normally the sender should have
+ done an ARP request to get its MAC address. Here we do it
+ in place of sending the packet and we hope that the sender
+ will retry sending its packet. */
+ memset(reh->h_dest, 0xff, ETH_ALEN);
+ memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
+ memcpy(&reh->h_source[2], &slirp->vhost_addr, 4);
+ reh->h_proto = htons(ETH_P_ARP);
+ rah->ar_hrd = htons(1);
+ rah->ar_pro = htons(ETH_P_IP);
+ rah->ar_hln = ETH_ALEN;
+ rah->ar_pln = 4;
+ rah->ar_op = htons(ARPOP_REQUEST);
+ /* source hw addr */
+ memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4);
+ memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4);
+ /* source IP */
+ rah->ar_sip = slirp->vhost_addr.s_addr;
+ /* target hw addr (none) */
+ memset(rah->ar_tha, 0, ETH_ALEN);
+ /* target IP */
+ rah->ar_tip = iph->ip_dst.s_addr;
+ slirp->client_ipaddr = iph->ip_dst;
+ slirp_output(slirp->opaque, arp_req, sizeof(arp_req));
+ } else {
+ memcpy(eh->h_dest, slirp->client_ethaddr, ETH_ALEN);
+ memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4);
+ /* XXX: not correct */
+ memcpy(&eh->h_source[2], &slirp->vhost_addr, 4);
+ eh->h_proto = htons(ETH_P_IP);
+ memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
+ slirp_output(slirp->opaque, buf, ip_data_len + ETH_HLEN);
+ }
+}
+
+/* Drop host forwarding rule, return 0 if found. */
+int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
+ int host_port)
+{
+ struct socket *so;
+ struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb);
+ struct sockaddr_in addr;
+ int port = htons(host_port);
+ socklen_t addr_len;
+
+ for (so = head->so_next; so != head; so = so->so_next) {
+ addr_len = sizeof(addr);
+ if ((so->so_state & SS_HOSTFWD) &&
+ getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&
+ addr.sin_addr.s_addr == host_addr.s_addr &&
+ addr.sin_port == port) {
+ close(so->s);
+ sofree(so);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
+ int host_port, struct in_addr guest_addr, int guest_port)
+{
+ if (!guest_addr.s_addr) {
+ guest_addr = slirp->vdhcp_startaddr;
+ }
+ if (is_udp) {
+ if (!udp_listen(slirp, host_addr.s_addr, htons(host_port),
+ guest_addr.s_addr, htons(guest_port), SS_HOSTFWD))
+ return -1;
+ } else {
+ if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port),
+ guest_addr.s_addr, htons(guest_port), SS_HOSTFWD))
+ return -1;
+ }
+ return 0;
+}
+
+int slirp_add_exec(Slirp *slirp, int do_pty, const void *args,
+ struct in_addr *guest_addr, int guest_port)
+{
+ if (!guest_addr->s_addr) {
+ guest_addr->s_addr = slirp->vnetwork_addr.s_addr |
+ (htonl(0x0204) & ~slirp->vnetwork_mask.s_addr);
+ }
+ if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) !=
+ slirp->vnetwork_addr.s_addr ||
+ guest_addr->s_addr == slirp->vhost_addr.s_addr ||
+ guest_addr->s_addr == slirp->vnameserver_addr.s_addr) {
+ return -1;
+ }
+ return add_exec(&slirp->exec_list, do_pty, (char *)args, *guest_addr,
+ htons(guest_port));
+}
+
+ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags)
+{
+#if 0
+ if (so->s == -1 && so->extra) {
+ qemu_chr_write(so->extra, buf, len);
+ return len;
+ }
+#endif
+ return send(so->s, buf, len, flags);
+}
+
+static struct socket *
+slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port)
+{
+ struct socket *so;
+
+ for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
+ if (so->so_faddr.s_addr == guest_addr.s_addr &&
+ htons(so->so_fport) == guest_port) {
+ return so;
+ }
+ }
+ return NULL;
+}
+
+size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
+ int guest_port)
+{
+ struct iovec iov[2];
+ struct socket *so;
+
+ so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);
+
+ if (!so || so->so_state & SS_NOFDREF)
+ return 0;
+
+ if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2))
+ return 0;
+
+ return sopreprbuf(so, iov, NULL);
+}
+
+void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
+ const uint8_t *buf, int size)
+{
+ int ret;
+ struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);
+
+ if (!so)
+ return;
+
+ ret = soreadbuf(so, (const char *)buf, size);
+
+ if (ret > 0)
+ tcp_output(sototcpcb(so));
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp.h
new file mode 100644
index 0000000..e2fc655
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp.h
@@ -0,0 +1,313 @@
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include <stdlib.h>
+#include "../cutils.h"
+#include "slirp_config.h"
+
+#ifdef _WIN32
+# include <inttypes.h>
+
+typedef char *caddr_t;
+
+# include <windows.h>
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <sys/timeb.h>
+# include <iphlpapi.h>
+
+# define EWOULDBLOCK WSAEWOULDBLOCK
+# define EINPROGRESS WSAEINPROGRESS
+# define ENOTCONN WSAENOTCONN
+# define EHOSTUNREACH WSAEHOSTUNREACH
+# define ENETUNREACH WSAENETUNREACH
+# define ECONNREFUSED WSAECONNREFUSED
+#else
+# define ioctlsocket ioctl
+# define closesocket(s) close(s)
+# if !defined(__HAIKU__)
+# define O_BINARY 0
+# endif
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_BITYPES_H
+# include <sys/bitypes.h>
+#endif
+
+#include <sys/time.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef HAVE_MEMMOVE
+#define memmove(x, y, z) bcopy(y, x, z)
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/uio.h>
+#endif
+
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+/* Systems lacking strdup() definition in <string.h>. */
+#if defined(ultrix)
+char *strdup(const char *);
+#endif
+
+/* Systems lacking malloc() definition in <stdlib.h>. */
+#if defined(ultrix) || defined(hcx)
+void *malloc(size_t arg);
+void free(void *ptr);
+#endif
+
+#ifndef HAVE_INET_ATON
+int inet_aton(const char *cp, struct in_addr *ia);
+#endif
+
+#include <fcntl.h>
+#ifndef NO_UNIX_SOCKETS
+#include <sys/un.h>
+#endif
+#include <signal.h>
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+#if defined(HAVE_SYS_IOCTL_H)
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#ifdef USE_PPP
+#include <ppp/slirppp.h>
+#endif
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <sys/stat.h>
+
+/* Avoid conflicting with the libc insque() and remque(), which
+ have different prototypes. */
+#define insque slirp_insque
+#define remque slirp_remque
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>
+#endif
+
+#include "debug.h"
+
+#include "libslirp.h"
+#include "ip.h"
+#include "tcp.h"
+#include "tcp_timer.h"
+#include "tcp_var.h"
+#include "tcpip.h"
+#include "udp.h"
+#include "mbuf.h"
+#include "sbuf.h"
+#include "socket.h"
+#include "if.h"
+#include "main.h"
+#include "misc.h"
+#ifdef USE_PPP
+#include "ppp/pppd.h"
+#include "ppp/ppp.h"
+#endif
+
+#include "bootp.h"
+#include "tftp.h"
+
+struct Slirp {
+ /* virtual network configuration */
+ struct in_addr vnetwork_addr;
+ struct in_addr vnetwork_mask;
+ struct in_addr vhost_addr;
+ struct in_addr vdhcp_startaddr;
+ struct in_addr vnameserver_addr;
+
+ /* ARP cache for the guest IP addresses (XXX: allow many entries) */
+ uint8_t client_ethaddr[6];
+
+ struct in_addr client_ipaddr;
+ char client_hostname[33];
+
+ int restricted;
+ struct timeval tt;
+ struct ex_list *exec_list;
+
+ /* mbuf states */
+ struct mbuf m_freelist, m_usedlist;
+ int mbuf_alloced;
+
+ /* if states */
+ int if_queued; /* number of packets queued so far */
+ struct mbuf if_fastq; /* fast queue (for interactive data) */
+ struct mbuf if_batchq; /* queue for non-interactive data */
+ struct mbuf *next_m; /* pointer to next mbuf to output */
+
+ /* ip states */
+ struct ipq ipq; /* ip reass. queue */
+ uint16_t ip_id; /* ip packet ctr, for ids */
+
+ /* bootp/dhcp states */
+ BOOTPClient bootp_clients[NB_BOOTP_CLIENTS];
+ char *bootp_filename;
+
+ /* tcp states */
+ struct socket tcb;
+ struct socket *tcp_last_so;
+ tcp_seq tcp_iss; /* tcp initial send seq # */
+ uint32_t tcp_now; /* for RFC 1323 timestamps */
+
+ /* udp states */
+ struct socket udb;
+ struct socket *udp_last_so;
+
+ /* tftp states */
+ char *tftp_prefix;
+ struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
+
+ void *opaque;
+};
+
+extern Slirp *slirp_instance;
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#ifndef FULL_BOLT
+void if_start(Slirp *);
+#else
+void if_start(struct ttys *);
+#endif
+
+#ifndef HAVE_STRERROR
+ char *strerror(int error);
+#endif
+
+#ifndef HAVE_INDEX
+ char *index(const char *, int);
+#endif
+
+#ifndef HAVE_GETHOSTID
+ long gethostid(void);
+#endif
+
+void lprint(const char *, ...) __attribute__((format(printf, 1, 2)));
+
+#ifndef _WIN32
+#include <netdb.h>
+#endif
+
+#define DEFAULT_BAUD 115200
+
+#define SO_OPTIONS DO_KEEPALIVE
+#define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL)
+
+/* cksum.c */
+int cksum(struct mbuf *m, int len);
+
+/* if.c */
+void if_init(Slirp *);
+void if_output(struct socket *, struct mbuf *);
+
+/* ip_input.c */
+void ip_init(Slirp *);
+void ip_input(struct mbuf *);
+void ip_slowtimo(Slirp *);
+void ip_stripoptions(register struct mbuf *, struct mbuf *);
+
+/* ip_output.c */
+int ip_output(struct socket *, struct mbuf *);
+
+/* tcp_input.c */
+void tcp_input(register struct mbuf *, int, struct socket *);
+int tcp_mss(register struct tcpcb *, u_int);
+
+/* tcp_output.c */
+int tcp_output(register struct tcpcb *);
+void tcp_setpersist(register struct tcpcb *);
+
+/* tcp_subr.c */
+void tcp_init(Slirp *);
+void tcp_template(struct tcpcb *);
+void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int);
+struct tcpcb * tcp_newtcpcb(struct socket *);
+struct tcpcb * tcp_close(register struct tcpcb *);
+void tcp_sockclosed(struct tcpcb *);
+int tcp_fconnect(struct socket *);
+void tcp_connect(struct socket *);
+int tcp_attach(struct socket *);
+uint8_t tcp_tos(struct socket *);
+int tcp_emu(struct socket *, struct mbuf *);
+int tcp_ctl(struct socket *);
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
+
+#ifdef USE_PPP
+#define MIN_MRU MINMRU
+#define MAX_MRU MAXMRU
+#else
+#define MIN_MRU 128
+#define MAX_MRU 16384
+#endif
+
+#ifndef _WIN32
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifdef _WIN32
+#undef errno
+#define errno (WSAGetLastError())
+#endif
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp_config.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp_config.h
new file mode 100644
index 0000000..18db45c
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/slirp_config.h
@@ -0,0 +1,188 @@
+/*
+ * User definable configuration options
+ */
+
+/* Define if you want the connection to be probed */
+/* XXX Not working yet, so ignore this for now */
+#undef PROBE_CONN
+
+/* Define to 1 if you want KEEPALIVE timers */
+#define DO_KEEPALIVE 0
+
+/* Define to MAX interfaces you expect to use at once */
+/* MAX_INTERFACES determines the max. TOTAL number of interfaces (SLIP and PPP) */
+/* MAX_PPP_INTERFACES determines max. number of PPP interfaces */
+#define MAX_INTERFACES 1
+#define MAX_PPP_INTERFACES 1
+
+/* Define if you want slirp's socket in /tmp */
+/* XXXXXX Do this in ./configure */
+#undef USE_TMPSOCKET
+
+/* Define if you want slirp to use cfsetXspeed() on the terminal */
+#undef DO_CFSETSPEED
+
+/* Define this if you want slirp to write to the tty as fast as it can */
+/* This should only be set if you are using load-balancing, slirp does a */
+/* pretty good job on single modems already, and seting this will make */
+/* interactive sessions less responsive */
+/* XXXXX Talk about having fast modem as unit 0 */
+#undef FULL_BOLT
+
+/*
+ * Define if you want slirp to use less CPU
+ * You will notice a small lag in interactive sessions, but it's not that bad
+ * Things like Netscape/ftp/etc. are completely unaffected
+ * This is mainly for sysadmins who have many slirp users
+ */
+#undef USE_LOWCPU
+
+/* Define this if your compiler doesn't like prototypes */
+#ifndef __STDC__
+#define NO_PROTOTYPES
+#endif
+
+/*********************************************************/
+/*
+ * Autoconf defined configuration options
+ * You shouldn't need to touch any of these
+ */
+
+/* Ignore this */
+#undef DUMMY_PPP
+
+/* Define if you have unistd.h */
+#define HAVE_UNISTD_H
+
+/* Define if you have stdlib.h */
+#define HAVE_STDLIB_H
+
+/* Define if you have sys/ioctl.h */
+#undef HAVE_SYS_IOCTL_H
+#ifndef _WIN32
+#define HAVE_SYS_IOCTL_H
+#endif
+
+/* Define if you have sys/filio.h */
+#undef HAVE_SYS_FILIO_H
+#ifdef __APPLE__
+#define HAVE_SYS_FILIO_H
+#endif
+
+/* Define if you have strerror */
+#define HAVE_STRERROR
+
+/* Define if you have strdup() */
+#define HAVE_STRDUP
+
+/* Define according to how time.h should be included */
+#define TIME_WITH_SYS_TIME 0
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have sys/bitypes.h */
+#undef HAVE_SYS_BITYPES_H
+
+/* Define if the machine is big endian */
+//#undef HOST_WORDS_BIGENDIAN
+
+/* Define if you have readv */
+#undef HAVE_READV
+
+/* Define if iovec needs to be declared */
+#undef DECLARE_IOVEC
+#ifdef _WIN32
+#define DECLARE_IOVEC
+#endif
+
+/* Define if you have a POSIX.1 sys/wait.h */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have sys/select.h */
+#undef HAVE_SYS_SELECT_H
+#ifndef _WIN32
+#define HAVE_SYS_SELECT_H
+#endif
+
+/* Define if you have strings.h */
+#define HAVE_STRING_H
+
+/* Define if you have arpa/inet.h */
+#undef HAVE_ARPA_INET_H
+#ifndef _WIN32
+#define HAVE_ARPA_INET_H
+#endif
+
+/* Define if you have sys/signal.h */
+#undef HAVE_SYS_SIGNAL_H
+
+/* Define if you have sys/stropts.h */
+#undef HAVE_SYS_STROPTS_H
+
+/* Define to whatever your compiler thinks inline should be */
+//#define inline inline
+
+/* Define to whatever your compiler thinks const should be */
+//#define const const
+
+/* Define if your compiler doesn't like prototypes */
+#undef NO_PROTOTYPES
+
+/* Define to sizeof(char) */
+#define SIZEOF_CHAR 1
+
+/* Define to sizeof(short) */
+#define SIZEOF_SHORT 2
+
+/* Define to sizeof(int) */
+#define SIZEOF_INT 4
+
+/* Define to sizeof(char *) */
+#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8)
+
+/* Define if you have random() */
+#undef HAVE_RANDOM
+
+/* Define if you have srandom() */
+#undef HAVE_SRANDOM
+
+/* Define if you have inet_aton */
+#undef HAVE_INET_ATON
+#ifndef _WIN32
+#define HAVE_INET_ATON
+#endif
+
+/* Define if you have setenv */
+#undef HAVE_SETENV
+
+/* Define if you have index() */
+#define HAVE_INDEX
+
+/* Define if you have bcmp() */
+#undef HAVE_BCMP
+
+/* Define if you have drand48 */
+#undef HAVE_DRAND48
+
+/* Define if you have memmove */
+#define HAVE_MEMMOVE
+
+/* Define if you have gethostid */
+#define HAVE_GETHOSTID
+
+/* Define if you DON'T have unix-domain sockets */
+#undef NO_UNIX_SOCKETS
+#ifdef _WIN32
+#define NO_UNIX_SOCKETS
+#endif
+
+/* Define if you have revoke() */
+#undef HAVE_REVOKE
+
+/* Define if you have the sysv method of opening pty's (/dev/ptmx, etc.) */
+#undef HAVE_GRANTPT
+
+/* Define if you have fchmod */
+#undef HAVE_FCHMOD
+
+/* Define if you have <sys/type32.h> */
+#undef HAVE_SYS_TYPES32_H
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/socket.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/socket.c
new file mode 100644
index 0000000..1ba8ae1
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/socket.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+static void sofcantrcvmore(struct socket *so);
+static void sofcantsendmore(struct socket *so);
+
+struct socket *
+solookup(struct socket *head, struct in_addr laddr, u_int lport,
+ struct in_addr faddr, u_int fport)
+{
+ struct socket *so;
+
+ for (so = head->so_next; so != head; so = so->so_next) {
+ if (so->so_lport == lport &&
+ so->so_laddr.s_addr == laddr.s_addr &&
+ so->so_faddr.s_addr == faddr.s_addr &&
+ so->so_fport == fport)
+ break;
+ }
+
+ if (so == head)
+ return (struct socket *)NULL;
+ return so;
+
+}
+
+/*
+ * Create a new socket, initialise the fields
+ * It is the responsibility of the caller to
+ * insque() it into the correct linked-list
+ */
+struct socket *
+socreate(Slirp *slirp)
+{
+ struct socket *so;
+
+ so = (struct socket *)malloc(sizeof(struct socket));
+ if(so) {
+ memset(so, 0, sizeof(struct socket));
+ so->so_state = SS_NOFDREF;
+ so->s = -1;
+ so->slirp = slirp;
+ }
+ return(so);
+}
+
+/*
+ * remque and free a socket, clobber cache
+ */
+void
+sofree(struct socket *so)
+{
+ Slirp *slirp = so->slirp;
+
+ if (so->so_emu==EMU_RSH && so->extra) {
+ sofree(so->extra);
+ so->extra=NULL;
+ }
+ if (so == slirp->tcp_last_so) {
+ slirp->tcp_last_so = &slirp->tcb;
+ } else if (so == slirp->udp_last_so) {
+ slirp->udp_last_so = &slirp->udb;
+ }
+ m_free(so->so_m);
+
+ if(so->so_next && so->so_prev)
+ remque(so); /* crashes if so is not in a queue */
+
+ free(so);
+}
+
+size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np)
+{
+ int n, lss, total;
+ struct sbuf *sb = &so->so_snd;
+ int len = sb->sb_datalen - sb->sb_cc;
+ int mss = so->so_tcpcb->t_maxseg;
+
+ DEBUG_CALL("sopreprbuf");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ if (len <= 0)
+ return 0;
+
+ iov[0].iov_base = sb->sb_wptr;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ if (sb->sb_wptr < sb->sb_rptr) {
+ iov[0].iov_len = sb->sb_rptr - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len)
+ iov[0].iov_len = len;
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ } else {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len) {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_rptr - sb->sb_data;
+ if(iov[1].iov_len > len)
+ iov[1].iov_len = len;
+ total = iov[0].iov_len + iov[1].iov_len;
+ if (total > mss) {
+ lss = total%mss;
+ if (iov[1].iov_len > lss) {
+ iov[1].iov_len -= lss;
+ n = 2;
+ } else {
+ lss -= iov[1].iov_len;
+ iov[0].iov_len -= lss;
+ n = 1;
+ }
+ } else
+ n = 2;
+ } else {
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ }
+ }
+ if (np)
+ *np = n;
+
+ return iov[0].iov_len + (n - 1) * iov[1].iov_len;
+}
+
+/*
+ * Read from so's socket into sb_snd, updating all relevant sbuf fields
+ * NOTE: This will only be called if it is select()ed for reading, so
+ * a read() of 0 (or less) means it's disconnected
+ */
+int
+soread(struct socket *so)
+{
+ int n, nn;
+ struct sbuf *sb = &so->so_snd;
+ struct iovec iov[2];
+
+ DEBUG_CALL("soread");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ /*
+ * No need to check if there's enough room to read.
+ * soread wouldn't have been called if there weren't
+ */
+ sopreprbuf(so, iov, &n);
+
+#ifdef HAVE_READV
+ nn = readv(so->s, (struct iovec *)iov, n);
+ DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#else
+ nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0);
+#endif
+ if (nn <= 0) {
+ if (nn < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+ else {
+ DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno)));
+ sofcantrcvmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ return -1;
+ }
+ }
+
+#ifndef HAVE_READV
+ /*
+ * If there was no error, try and read the second time round
+ * We read again if n = 2 (ie, there's another part of the buffer)
+ * and we read as much as we could in the first read
+ * We don't test for <= 0 this time, because there legitimately
+ * might not be any more data (since the socket is non-blocking),
+ * a close will be detected on next iteration.
+ * A return of -1 wont (shouldn't) happen, since it didn't happen above
+ */
+ if (n == 2 && nn == iov[0].iov_len) {
+ int ret;
+ ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0);
+ if (ret > 0)
+ nn += ret;
+ }
+
+ DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#endif
+
+ /* Update fields */
+ sb->sb_cc += nn;
+ sb->sb_wptr += nn;
+ if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_wptr -= sb->sb_datalen;
+ return nn;
+}
+
+int soreadbuf(struct socket *so, const char *buf, int size)
+{
+ int n, nn, copy = size;
+ struct sbuf *sb = &so->so_snd;
+ struct iovec iov[2];
+
+ DEBUG_CALL("soreadbuf");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ /*
+ * No need to check if there's enough room to read.
+ * soread wouldn't have been called if there weren't
+ */
+ if (sopreprbuf(so, iov, &n) < size)
+ goto err;
+
+ nn = min(iov[0].iov_len, copy);
+ memcpy(iov[0].iov_base, buf, nn);
+
+ copy -= nn;
+ buf += nn;
+
+ if (copy == 0)
+ goto done;
+
+ memcpy(iov[1].iov_base, buf, copy);
+
+done:
+ /* Update fields */
+ sb->sb_cc += size;
+ sb->sb_wptr += size;
+ if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_wptr -= sb->sb_datalen;
+ return size;
+err:
+
+ sofcantrcvmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ fprintf(stderr, "soreadbuf buffer to small");
+ return -1;
+}
+
+/*
+ * Get urgent data
+ *
+ * When the socket is created, we set it SO_OOBINLINE,
+ * so when OOB data arrives, we soread() it and everything
+ * in the send buffer is sent as urgent data
+ */
+void
+sorecvoob(struct socket *so)
+{
+ struct tcpcb *tp = sototcpcb(so);
+
+ DEBUG_CALL("sorecvoob");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ /*
+ * We take a guess at how much urgent data has arrived.
+ * In most situations, when urgent data arrives, the next
+ * read() should get all the urgent data. This guess will
+ * be wrong however if more data arrives just after the
+ * urgent data, or the read() doesn't return all the
+ * urgent data.
+ */
+ soread(so);
+ tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
+ tp->t_force = 1;
+ tcp_output(tp);
+ tp->t_force = 0;
+}
+
+/*
+ * Send urgent data
+ * There's a lot duplicated code here, but...
+ */
+int
+sosendoob(struct socket *so)
+{
+ struct sbuf *sb = &so->so_rcv;
+ char buff[2048]; /* XXX Shouldn't be sending more oob data than this */
+
+ int n, len;
+
+ DEBUG_CALL("sosendoob");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc);
+
+ if (so->so_urgc > 2048)
+ so->so_urgc = 2048; /* XXXX */
+
+ if (sb->sb_rptr < sb->sb_wptr) {
+ /* We can send it directly */
+ n = slirp_send(so, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+ so->so_urgc -= n;
+
+ DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+ } else {
+ /*
+ * Since there's no sendv or sendtov like writev,
+ * we must copy all data to a linear buffer then
+ * send it all
+ */
+ len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (len > so->so_urgc) len = so->so_urgc;
+ memcpy(buff, sb->sb_rptr, len);
+ so->so_urgc -= len;
+ if (so->so_urgc) {
+ n = sb->sb_wptr - sb->sb_data;
+ if (n > so->so_urgc) n = so->so_urgc;
+ memcpy((buff + len), sb->sb_data, n);
+ so->so_urgc -= n;
+ len += n;
+ }
+ n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+#ifdef DEBUG
+ if (n != len)
+ DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n"));
+#endif
+ DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+ }
+
+ sb->sb_cc -= n;
+ sb->sb_rptr += n;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ return n;
+}
+
+/*
+ * Write data from so_rcv to so's socket,
+ * updating all sbuf field as necessary
+ */
+int
+sowrite(struct socket *so)
+{
+ int n,nn;
+ struct sbuf *sb = &so->so_rcv;
+ int len = sb->sb_cc;
+ struct iovec iov[2];
+
+ DEBUG_CALL("sowrite");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (so->so_urgc) {
+ sosendoob(so);
+ if (sb->sb_cc == 0)
+ return 0;
+ }
+
+ /*
+ * No need to check if there's something to write,
+ * sowrite wouldn't have been called otherwise
+ */
+
+ iov[0].iov_base = sb->sb_rptr;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ if (sb->sb_rptr < sb->sb_wptr) {
+ iov[0].iov_len = sb->sb_wptr - sb->sb_rptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ n = 1;
+ } else {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len) {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_wptr - sb->sb_data;
+ if (iov[1].iov_len > len) iov[1].iov_len = len;
+ n = 2;
+ } else
+ n = 1;
+ }
+ /* Check if there's urgent data to send, and if so, send it */
+
+#ifdef HAVE_READV
+ nn = writev(so->s, (const struct iovec *)iov, n);
+
+ DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
+#else
+ nn = slirp_send(so, iov[0].iov_base, iov[0].iov_len,0);
+#endif
+ /* This should never happen, but people tell me it does *shrug* */
+ if (nn < 0 && (errno == EAGAIN || errno == EINTR))
+ return 0;
+
+ if (nn <= 0) {
+ DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n",
+ so->so_state, errno));
+ sofcantsendmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ return -1;
+ }
+
+#ifndef HAVE_READV
+ if (n == 2 && nn == iov[0].iov_len) {
+ int ret;
+ ret = slirp_send(so, iov[1].iov_base, iov[1].iov_len,0);
+ if (ret > 0)
+ nn += ret;
+ }
+ DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
+#endif
+
+ /* Update sbuf */
+ sb->sb_cc -= nn;
+ sb->sb_rptr += nn;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ /*
+ * If in DRAIN mode, and there's no more data, set
+ * it CANTSENDMORE
+ */
+ if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0)
+ sofcantsendmore(so);
+
+ return nn;
+}
+
+/*
+ * recvfrom() a UDP socket
+ */
+void
+sorecvfrom(struct socket *so)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+
+ DEBUG_CALL("sorecvfrom");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
+ char buff[256];
+ int len;
+
+ len = recvfrom(so->s, buff, 256, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ /* XXX Check if reply is "correct"? */
+
+ if(len == -1 || len == 0) {
+ u_char code=ICMP_UNREACH_PORT;
+
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+ DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n",
+ errno,strerror(errno)));
+ icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ } else {
+ icmp_reflect(so->so_m);
+ so->so_m = NULL; /* Don't m_free() it again! */
+ }
+ /* No need for this socket anymore, udp_detach it */
+ udp_detach(so);
+ } else { /* A "normal" UDP packet */
+ struct mbuf *m;
+ int len;
+#ifdef _WIN32
+ unsigned long n;
+#else
+ int n;
+#endif
+
+ m = m_get(so->slirp);
+ if (!m) {
+ return;
+ }
+ m->m_data += IF_MAXLINKHDR;
+
+ /*
+ * XXX Shouldn't FIONREAD packets destined for port 53,
+ * but I don't know the max packet size for DNS lookups
+ */
+ len = M_FREEROOM(m);
+ /* if (so->so_fport != htons(53)) { */
+ ioctlsocket(so->s, FIONREAD, &n);
+
+ if (n > len) {
+ n = (m->m_data - m->m_dat) + m->m_len + n + 1;
+ m_inc(m, n);
+ len = M_FREEROOM(m);
+ }
+ /* } */
+
+ m->m_len = recvfrom(so->s, m->m_data, len, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n",
+ m->m_len, errno,strerror(errno)));
+ if(m->m_len<0) {
+ u_char code=ICMP_UNREACH_PORT;
+
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+ DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code));
+ icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ m_free(m);
+ } else {
+ /*
+ * Hack: domain name lookup will be used the most for UDP,
+ * and since they'll only be used once there's no need
+ * for the 4 minute (or whatever) timeout... So we time them
+ * out much quicker (10 seconds for now...)
+ */
+ if (so->so_expire) {
+ if (so->so_fport == htons(53))
+ so->so_expire = curtime + SO_EXPIREFAST;
+ else
+ so->so_expire = curtime + SO_EXPIRE;
+ }
+
+ /*
+ * If this packet was destined for CTL_ADDR,
+ * make it look like that's where it came from, done by udp_output
+ */
+ udp_output(so, m, &addr);
+ } /* rx error */
+ } /* if ping packet */
+}
+
+/*
+ * sendto() a socket
+ */
+int
+sosendto(struct socket *so, struct mbuf *m)
+{
+ Slirp *slirp = so->slirp;
+ int ret;
+ struct sockaddr_in addr;
+
+ DEBUG_CALL("sosendto");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ /* It's an alias */
+ if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
+ if (get_dns_addr(&addr.sin_addr) < 0)
+ addr.sin_addr = loopback_addr;
+ } else {
+ addr.sin_addr = loopback_addr;
+ }
+ } else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+
+ /* Don't care what port we get */
+ ret = sendto(so->s, m->m_data, m->m_len, 0,
+ (struct sockaddr *)&addr, sizeof (struct sockaddr));
+ if (ret < 0)
+ return -1;
+
+ /*
+ * Kill the socket if there's no reply in 4 minutes,
+ * but only if it's an expirable socket
+ */
+ if (so->so_expire)
+ so->so_expire = curtime + SO_EXPIRE;
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_ISFCONNECTED; /* So that it gets select()ed */
+ return 0;
+}
+
+/*
+ * Listen for incoming TCP connections
+ */
+struct socket *
+tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
+ u_int lport, int flags)
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ int s, opt = 1;
+ socklen_t addrlen = sizeof(addr);
+ memset(&addr, 0, addrlen);
+
+ DEBUG_CALL("tcp_listen");
+ DEBUG_ARG("haddr = %x", haddr);
+ DEBUG_ARG("hport = %d", hport);
+ DEBUG_ARG("laddr = %x", laddr);
+ DEBUG_ARG("lport = %d", lport);
+ DEBUG_ARG("flags = %x", flags);
+
+ so = socreate(slirp);
+ if (!so) {
+ return NULL;
+ }
+
+ /* Don't tcp_attach... we don't need so_snd nor so_rcv */
+ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) {
+ free(so);
+ return NULL;
+ }
+ insque(so, &slirp->tcb);
+
+ /*
+ * SS_FACCEPTONCE sockets must time out.
+ */
+ if (flags & SS_FACCEPTONCE)
+ so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2;
+
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= (SS_FACCEPTCONN | flags);
+ so->so_lport = lport; /* Kept in network format */
+ so->so_laddr.s_addr = laddr; /* Ditto */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = haddr;
+ addr.sin_port = hport;
+
+ if (((s = os_socket(AF_INET,SOCK_STREAM,0)) < 0) ||
+ (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) ||
+ (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) ||
+ (listen(s,1) < 0)) {
+ int tmperrno = errno; /* Don't clobber the real reason we failed */
+
+ close(s);
+ sofree(so);
+ /* Restore the real errno */
+#ifdef _WIN32
+ WSASetLastError(tmperrno);
+#else
+ errno = tmperrno;
+#endif
+ return NULL;
+ }
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+
+ getsockname(s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = slirp->vhost_addr;
+ else
+ so->so_faddr = addr.sin_addr;
+
+ so->s = s;
+ return so;
+}
+
+/*
+ * Various session state calls
+ * XXX Should be #define's
+ * The socket state stuff needs work, these often get call 2 or 3
+ * times each when only 1 was needed
+ */
+void
+soisfconnecting(struct socket *so)
+{
+ so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE|
+ SS_FCANTSENDMORE|SS_FWDRAIN);
+ so->so_state |= SS_ISFCONNECTING; /* Clobber other states */
+}
+
+void
+soisfconnected(struct socket *so)
+{
+ so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF);
+ so->so_state |= SS_ISFCONNECTED; /* Clobber other states */
+}
+
+static void
+sofcantrcvmore(struct socket *so)
+{
+ if ((so->so_state & SS_NOFDREF) == 0) {
+ shutdown(so->s,0);
+ if(global_writefds) {
+ FD_CLR(so->s,global_writefds);
+ }
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTSENDMORE) {
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF; /* Don't select it */
+ } else {
+ so->so_state |= SS_FCANTRCVMORE;
+ }
+}
+
+static void
+sofcantsendmore(struct socket *so)
+{
+ if ((so->so_state & SS_NOFDREF) == 0) {
+ shutdown(so->s,1); /* send FIN to fhost */
+ if (global_readfds) {
+ FD_CLR(so->s,global_readfds);
+ }
+ if (global_xfds) {
+ FD_CLR(so->s,global_xfds);
+ }
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTRCVMORE) {
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF; /* as above */
+ } else {
+ so->so_state |= SS_FCANTSENDMORE;
+ }
+}
+
+/*
+ * Set write drain mode
+ * Set CANTSENDMORE once all data has been write()n
+ */
+void
+sofwdrain(struct socket *so)
+{
+ if (so->so_rcv.sb_cc)
+ so->so_state |= SS_FWDRAIN;
+ else
+ sofcantsendmore(so);
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/socket.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/socket.h
new file mode 100644
index 0000000..857b0da
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/socket.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _SLIRP_SOCKET_H_
+#define _SLIRP_SOCKET_H_
+
+#define SO_EXPIRE 240000
+#define SO_EXPIREFAST 10000
+
+/*
+ * Our socket structure
+ */
+
+struct socket {
+ struct socket *so_next,*so_prev; /* For a linked list of sockets */
+
+ int s; /* The actual socket */
+
+ Slirp *slirp; /* managing slirp instance */
+
+ /* XXX union these with not-yet-used sbuf params */
+ struct mbuf *so_m; /* Pointer to the original SYN packet,
+ * for non-blocking connect()'s, and
+ * PING reply's */
+ struct tcpiphdr *so_ti; /* Pointer to the original ti within
+ * so_mconn, for non-blocking connections */
+ int so_urgc;
+ struct in_addr so_faddr; /* foreign host table entry */
+ struct in_addr so_laddr; /* local host table entry */
+ uint16_t so_fport; /* foreign port */
+ uint16_t so_lport; /* local port */
+
+ uint8_t so_iptos; /* Type of service */
+ uint8_t so_emu; /* Is the socket emulated? */
+
+ u_char so_type; /* Type of socket, UDP or TCP */
+ int so_state; /* internal state flags SS_*, below */
+
+ struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */
+ u_int so_expire; /* When the socket will expire */
+
+ int so_queued; /* Number of packets queued from this socket */
+ int so_nqueued; /* Number of packets queued in a row
+ * Used to determine when to "downgrade" a session
+ * from fastq to batchq */
+
+ struct sbuf so_rcv; /* Receive buffer */
+ struct sbuf so_snd; /* Send buffer */
+ void * extra; /* Extra pointer */
+};
+
+
+/*
+ * Socket state bits. (peer means the host on the Internet,
+ * local host means the host on the other end of the modem)
+ */
+#define SS_NOFDREF 0x001 /* No fd reference */
+
+#define SS_ISFCONNECTING 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */
+#define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */
+#define SS_FCANTRCVMORE 0x008 /* Socket can't receive more from peer (for half-closes) */
+#define SS_FCANTSENDMORE 0x010 /* Socket can't send more to peer (for half-closes) */
+#define SS_FWDRAIN 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */
+
+#define SS_CTL 0x080
+#define SS_FACCEPTCONN 0x100 /* Socket is accepting connections from a host on the internet */
+#define SS_FACCEPTONCE 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */
+
+#define SS_PERSISTENT_MASK 0xf000 /* Unremovable state bits */
+#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */
+#define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */
+
+struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int);
+struct socket * socreate(Slirp *);
+void sofree(struct socket *);
+int soread(struct socket *);
+void sorecvoob(struct socket *);
+int sosendoob(struct socket *);
+int sowrite(struct socket *);
+void sorecvfrom(struct socket *);
+int sosendto(struct socket *, struct mbuf *);
+struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
+ int);
+void soisfconnecting(register struct socket *);
+void soisfconnected(register struct socket *);
+void sofwdrain(struct socket *);
+struct iovec; /* For win32 */
+size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np);
+int soreadbuf(struct socket *so, const char *buf, int size);
+
+#endif /* _SOCKET_H_ */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp.h
new file mode 100644
index 0000000..9d06836
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
+ * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp
+ */
+
+#ifndef _TCP_H_
+#define _TCP_H_
+
+typedef uint32_t tcp_seq;
+
+#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
+#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
+
+#define TCP_SNDSPACE 8192
+#define TCP_RCVSPACE 8192
+
+/*
+ * TCP header.
+ * Per RFC 793, September, 1981.
+ */
+struct tcphdr {
+ uint16_t th_sport; /* source port */
+ uint16_t th_dport; /* destination port */
+ tcp_seq th_seq; /* sequence number */
+ tcp_seq th_ack; /* acknowledgement number */
+#ifdef HOST_WORDS_BIGENDIAN
+ u_int th_off:4, /* data offset */
+ th_x2:4; /* (unused) */
+#else
+ u_int th_x2:4, /* (unused) */
+ th_off:4; /* data offset */
+#endif
+ uint8_t th_flags;
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+ uint16_t th_win; /* window */
+ uint16_t th_sum; /* checksum */
+ uint16_t th_urp; /* urgent pointer */
+};
+
+#include "tcp_var.h"
+
+#define TCPOPT_EOL 0
+#define TCPOPT_NOP 1
+#define TCPOPT_MAXSEG 2
+#define TCPOLEN_MAXSEG 4
+#define TCPOPT_WINDOW 3
+#define TCPOLEN_WINDOW 3
+#define TCPOPT_SACK_PERMITTED 4 /* Experimental */
+#define TCPOLEN_SACK_PERMITTED 2
+#define TCPOPT_SACK 5 /* Experimental */
+#define TCPOPT_TIMESTAMP 8
+#define TCPOLEN_TIMESTAMP 10
+#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
+
+#define TCPOPT_TSTAMP_HDR \
+ (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
+
+/*
+ * Default maximum segment size for TCP.
+ * With an IP MSS of 576, this is 536,
+ * but 512 is probably more convenient.
+ * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
+ *
+ * We make this 1460 because we only care about Ethernet in the qemu context.
+ */
+#define TCP_MSS 1460
+
+#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
+
+#define TCP_MAX_WINSHIFT 14 /* maximum window shift */
+
+/*
+ * User-settable options (used with setsockopt).
+ *
+ * We don't use the system headers on unix because we have conflicting
+ * local structures. We can't avoid the system definitions on Windows,
+ * so we undefine them.
+ */
+#undef TCP_NODELAY
+#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
+#undef TCP_MAXSEG
+
+/*
+ * TCP FSM state definitions.
+ * Per RFC793, September, 1981.
+ */
+
+#define TCP_NSTATES 11
+
+#define TCPS_CLOSED 0 /* closed */
+#define TCPS_LISTEN 1 /* listening for connection */
+#define TCPS_SYN_SENT 2 /* active, have sent syn */
+#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
+/* states < TCPS_ESTABLISHED are those where connections not established */
+#define TCPS_ESTABLISHED 4 /* established */
+#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
+/* states > TCPS_CLOSE_WAIT are those where user has closed */
+#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
+#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
+#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
+/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
+#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
+#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
+
+#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED)
+#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED)
+#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT)
+
+/*
+ * TCP sequence numbers are 32 bit integers operated
+ * on with modular arithmetic. These macros can be
+ * used to compare such integers.
+ */
+#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
+#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Macros to initialize tcp sequence numbers for
+ * send and receive from initial send and receive
+ * sequence numbers.
+ */
+#define tcp_rcvseqinit(tp) \
+ (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1
+
+#define tcp_sendseqinit(tp) \
+ (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss
+
+#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_input.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_input.c
new file mode 100644
index 0000000..53dbc87
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_input.c
@@ -0,0 +1,1487 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94
+ * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+#define TCPREXMTTHRESH 3
+
+#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
+
+/* for modulo comparisons of timestamps */
+#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0)
+#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Insert segment ti into reassembly queue of tcp with
+ * control block tp. Return TH_FIN if reassembly now includes
+ * a segment with FIN. The macro form does the common case inline
+ * (segment is the next to be received on an established connection,
+ * and the queue is empty), avoiding linkage into and removal
+ * from the queue and repetition of various conversions.
+ * Set DELACK for segments received in order, but ack immediately
+ * when segments are out of order (so fast retransmit can work).
+ */
+#ifdef TCP_ACK_HACK
+#define TCP_REASS(tp, ti, m, so, flags) {\
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ tcpfrag_list_empty(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) {\
+ if (ti->ti_flags & TH_PUSH) \
+ tp->t_flags |= TF_ACKNOW; \
+ else \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ if (so->so_emu) { \
+ if (tcp_emu((so),(m))) sbappend((so), (m)); \
+ } else \
+ sbappend((so), (m)); \
+ } else {\
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#else
+#define TCP_REASS(tp, ti, m, so, flags) { \
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ tcpfrag_list_empty(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) { \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ if (so->so_emu) { \
+ if (tcp_emu((so),(m))) sbappend(so, (m)); \
+ } else \
+ sbappend((so), (m)); \
+ } else { \
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#endif
+static void tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt,
+ struct tcpiphdr *ti);
+static void tcp_xmit_timer(register struct tcpcb *tp, int rtt);
+
+static int
+tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti,
+ struct mbuf *m)
+{
+ register struct tcpiphdr *q;
+ struct socket *so = tp->t_socket;
+ int flags;
+
+ /*
+ * Call with ti==NULL after become established to
+ * force pre-ESTABLISHED data up to user socket.
+ */
+ if (ti == NULL)
+ goto present;
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = tcpfrag_list_first(tp); !tcpfrag_list_end(q, tp);
+ q = tcpiphdr_next(q))
+ if (SEQ_GT(q->ti_seq, ti->ti_seq))
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if (!tcpfrag_list_end(tcpiphdr_prev(q), tp)) {
+ register int i;
+ q = tcpiphdr_prev(q);
+ /* conversion to int (in i) handles seq wraparound */
+ i = q->ti_seq + q->ti_len - ti->ti_seq;
+ if (i > 0) {
+ if (i >= ti->ti_len) {
+ m_freem(m);
+ /*
+ * Try to present any queued data
+ * at the left window edge to the user.
+ * This is needed after the 3-WHS
+ * completes.
+ */
+ goto present; /* ??? */
+ }
+ m_adj(m, i);
+ ti->ti_len -= i;
+ ti->ti_seq += i;
+ }
+ q = tcpiphdr_next(q);
+ }
+ ti->ti_mbuf = m;
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (!tcpfrag_list_end(q, tp)) {
+ register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq;
+ if (i <= 0)
+ break;
+ if (i < q->ti_len) {
+ q->ti_seq += i;
+ q->ti_len -= i;
+ m_adj(q->ti_mbuf, i);
+ break;
+ }
+ q = tcpiphdr_next(q);
+ m = tcpiphdr_prev(q)->ti_mbuf;
+ remque(tcpiphdr2qlink(tcpiphdr_prev(q)));
+ m_freem(m);
+ }
+
+ /*
+ * Stick new segment in its place.
+ */
+ insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q)));
+
+present:
+ /*
+ * Present data to user, advancing rcv_nxt through
+ * completed sequence space.
+ */
+ if (!TCPS_HAVEESTABLISHED(tp->t_state))
+ return (0);
+ ti = tcpfrag_list_first(tp);
+ if (tcpfrag_list_end(ti, tp) || ti->ti_seq != tp->rcv_nxt)
+ return (0);
+ if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len)
+ return (0);
+ do {
+ tp->rcv_nxt += ti->ti_len;
+ flags = ti->ti_flags & TH_FIN;
+ remque(tcpiphdr2qlink(ti));
+ m = ti->ti_mbuf;
+ ti = tcpiphdr_next(ti);
+ if (so->so_state & SS_FCANTSENDMORE)
+ m_freem(m);
+ else {
+ if (so->so_emu) {
+ if (tcp_emu(so,m)) sbappend(so, m);
+ } else
+ sbappend(so, m);
+ }
+ } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt);
+ return (flags);
+}
+
+/*
+ * TCP input routine, follows pages 65-76 of the
+ * protocol specification dated September, 1981 very closely.
+ */
+void
+tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
+{
+ struct ip save_ip, *ip;
+ register struct tcpiphdr *ti;
+ caddr_t optp = NULL;
+ int optlen = 0;
+ int len, tlen, off;
+ register struct tcpcb *tp = NULL;
+ register int tiflags;
+ struct socket *so = NULL;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+ int iss = 0;
+ u_long tiwin;
+ int ret;
+ struct ex_list *ex_ptr;
+ Slirp *slirp;
+
+ DEBUG_CALL("tcp_input");
+ DEBUG_ARGS((dfd," m = %8lx iphlen = %2d inso = %lx\n",
+ (long )m, iphlen, (long )inso ));
+
+ /*
+ * If called with m == 0, then we're continuing the connect
+ */
+ if (m == NULL) {
+ so = inso;
+ slirp = so->slirp;
+
+ /* Re-set a few variables */
+ tp = sototcpcb(so);
+ m = so->so_m;
+ so->so_m = NULL;
+ ti = so->so_ti;
+ tiwin = ti->ti_win;
+ tiflags = ti->ti_flags;
+
+ goto cont_conn;
+ }
+ slirp = m->slirp;
+
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ ti = mtod(m, struct tcpiphdr *);
+ if (iphlen > sizeof(struct ip )) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen=sizeof(struct ip );
+ }
+ /* XXX Check if too short */
+
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ ip=mtod(m, struct ip *);
+ save_ip = *ip;
+ save_ip.ip_len+= iphlen;
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ((struct ip *)ti)->ip_len;
+ tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
+ memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+ ti->ti_x1 = 0;
+ ti->ti_len = htons((uint16_t)tlen);
+ len = sizeof(struct ip ) + tlen;
+ if(cksum(m, len)) {
+ goto drop;
+ }
+
+ /*
+ * Check that TCP offset makes sense,
+ * pull out TCP options and adjust length. XXX
+ */
+ off = ti->ti_off << 2;
+ if (off < sizeof (struct tcphdr) || off > tlen) {
+ goto drop;
+ }
+ tlen -= off;
+ ti->ti_len = tlen;
+ if (off > sizeof (struct tcphdr)) {
+ optlen = off - sizeof (struct tcphdr);
+ optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr);
+ }
+ tiflags = ti->ti_flags;
+
+ /*
+ * Convert TCP protocol specific fields to host format.
+ */
+ NTOHL(ti->ti_seq);
+ NTOHL(ti->ti_ack);
+ NTOHS(ti->ti_win);
+ NTOHS(ti->ti_urp);
+
+ /*
+ * Drop TCP, IP headers and TCP options.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+
+ if (slirp->restricted) {
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_fport == ti->ti_dport &&
+ ti->ti_dst.s_addr == ex_ptr->ex_addr.s_addr) {
+ break;
+ }
+ }
+ if (!ex_ptr)
+ goto drop;
+ }
+ /*
+ * Locate pcb for segment.
+ */
+findso:
+ so = slirp->tcp_last_so;
+ if (so->so_fport != ti->ti_dport ||
+ so->so_lport != ti->ti_sport ||
+ so->so_laddr.s_addr != ti->ti_src.s_addr ||
+ so->so_faddr.s_addr != ti->ti_dst.s_addr) {
+ so = solookup(&slirp->tcb, ti->ti_src, ti->ti_sport,
+ ti->ti_dst, ti->ti_dport);
+ if (so)
+ slirp->tcp_last_so = so;
+ }
+
+ /*
+ * If the state is CLOSED (i.e., TCB does not exist) then
+ * all data in the incoming segment is discarded.
+ * If the TCB exists but is in CLOSED state, it is embryonic,
+ * but should either do a listen or a connect soon.
+ *
+ * state == CLOSED means we've done socreate() but haven't
+ * attached it to a protocol yet...
+ *
+ * XXX If a TCB does not exist, and the TH_SYN flag is
+ * the only flag set, then create a session, mark it
+ * as if it was LISTENING, and continue...
+ */
+ if (so == NULL) {
+ if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN)
+ goto dropwithreset;
+
+ if ((so = socreate(slirp)) == NULL)
+ goto dropwithreset;
+ if (tcp_attach(so) < 0) {
+ free(so); /* Not sofree (if it failed, it's not insqued) */
+ goto dropwithreset;
+ }
+
+ sbreserve(&so->so_snd, TCP_SNDSPACE);
+ sbreserve(&so->so_rcv, TCP_RCVSPACE);
+
+ so->so_laddr = ti->ti_src;
+ so->so_lport = ti->ti_sport;
+ so->so_faddr = ti->ti_dst;
+ so->so_fport = ti->ti_dport;
+
+ if ((so->so_iptos = tcp_tos(so)) == 0)
+ so->so_iptos = ((struct ip *)ti)->ip_tos;
+
+ tp = sototcpcb(so);
+ tp->t_state = TCPS_LISTEN;
+ }
+
+ /*
+ * If this is a still-connecting socket, this probably
+ * a retransmit of the SYN. Whether it's a retransmit SYN
+ * or something else, we nuke it.
+ */
+ if (so->so_state & SS_ISFCONNECTING)
+ goto drop;
+
+ tp = sototcpcb(so);
+
+ /* XXX Should never fail */
+ if (tp == NULL)
+ goto dropwithreset;
+ if (tp->t_state == TCPS_CLOSED)
+ goto drop;
+
+ tiwin = ti->ti_win;
+
+ /*
+ * Segment received on connection.
+ * Reset idle time and keep-alive timer.
+ */
+ tp->t_idle = 0;
+ if (SO_OPTIONS)
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
+ else
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
+
+ /*
+ * Process options if not in LISTEN state,
+ * else do it below (after getting remote address).
+ */
+ if (optp && tp->t_state != TCPS_LISTEN)
+ tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+
+ /*
+ * Header prediction: check for the two common cases
+ * of a uni-directional data xfer. If the packet has
+ * no control flags, is in-sequence, the window didn't
+ * change and we're not retransmitting, it's a
+ * candidate. If the length is zero and the ack moved
+ * forward, we're the sender side of the xfer. Just
+ * free the data acked & wake any higher level process
+ * that was blocked waiting for space. If the length
+ * is non-zero and the ack didn't move, we're the
+ * receiver side. If we're getting packets in-order
+ * (the reassembly queue is empty), add the data to
+ * the socket buffer and note that we need a delayed ack.
+ *
+ * XXX Some of these tests are not needed
+ * eg: the tiwin == tp->snd_wnd prevents many more
+ * predictions.. with no *real* advantage..
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
+ ti->ti_seq == tp->rcv_nxt &&
+ tiwin && tiwin == tp->snd_wnd &&
+ tp->snd_nxt == tp->snd_max) {
+ if (ti->ti_len == 0) {
+ if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
+ SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
+ tp->snd_cwnd >= tp->snd_wnd) {
+ /*
+ * this is a pure ack for outstanding data.
+ */
+ if (tp->t_rtt &&
+ SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp, tp->t_rtt);
+ acked = ti->ti_ack - tp->snd_una;
+ sbdrop(&so->so_snd, acked);
+ tp->snd_una = ti->ti_ack;
+ m_freem(m);
+
+ /*
+ * If all outstanding data are acked, stop
+ * retransmit timer, otherwise restart timer
+ * using current (possibly backed-off) value.
+ * If process is waiting for space,
+ * wakeup/selwakeup/signal. If data
+ * are ready to send, let tcp_output
+ * decide between more output or persist.
+ */
+ if (tp->snd_una == tp->snd_max)
+ tp->t_timer[TCPT_REXMT] = 0;
+ else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+
+ /*
+ * This is called because sowwakeup might have
+ * put data into so_snd. Since we don't so sowwakeup,
+ * we don't need this.. XXX???
+ */
+ if (so->so_snd.sb_cc)
+ (void) tcp_output(tp);
+
+ return;
+ }
+ } else if (ti->ti_ack == tp->snd_una &&
+ tcpfrag_list_empty(tp) &&
+ ti->ti_len <= sbspace(&so->so_rcv)) {
+ /*
+ * this is a pure, in-sequence data packet
+ * with nothing on the reassembly queue and
+ * we have enough buffer space to take it.
+ */
+ tp->rcv_nxt += ti->ti_len;
+ /*
+ * Add data to socket buffer.
+ */
+ if (so->so_emu) {
+ if (tcp_emu(so,m)) sbappend(so, m);
+ } else
+ sbappend(so, m);
+
+ /*
+ * If this is a short packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * It is better to not delay acks at all to maximize
+ * TCP throughput. See RFC 2581.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ tcp_output(tp);
+ return;
+ }
+ } /* header prediction */
+ /*
+ * Calculate amount of space in receive window,
+ * and then do TCP input processing.
+ * Receive window is amount of space in rcv queue,
+ * but not less than advertised window.
+ */
+ { int win;
+ win = sbspace(&so->so_rcv);
+ if (win < 0)
+ win = 0;
+ tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
+ }
+
+ switch (tp->t_state) {
+
+ /*
+ * If the state is LISTEN then ignore segment if it contains an RST.
+ * If the segment contains an ACK then it is bad and send a RST.
+ * If it does not contain a SYN then it is not interesting; drop it.
+ * Don't bother responding if the destination was a broadcast.
+ * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
+ * tp->iss, and send a segment:
+ * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
+ * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
+ * Fill in remote peer address fields if not previously specified.
+ * Enter SYN_RECEIVED state, and process any other fields of this
+ * segment in this state.
+ */
+ case TCPS_LISTEN: {
+
+ if (tiflags & TH_RST)
+ goto drop;
+ if (tiflags & TH_ACK)
+ goto dropwithreset;
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+
+ /*
+ * This has way too many gotos...
+ * But a bit of spaghetti code never hurt anybody :)
+ */
+
+ /*
+ * If this is destined for the control address, then flag to
+ * tcp_ctl once connected, otherwise connect
+ */
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr &&
+ so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) {
+ /* May be an add exec */
+ for (ex_ptr = slirp->exec_list; ex_ptr;
+ ex_ptr = ex_ptr->ex_next) {
+ if(ex_ptr->ex_fport == so->so_fport &&
+ so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
+ so->so_state |= SS_CTL;
+ break;
+ }
+ }
+ if (so->so_state & SS_CTL) {
+ goto cont_input;
+ }
+ }
+ /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */
+ }
+
+ if (so->so_emu & EMU_NOCONNECT) {
+ so->so_emu &= ~EMU_NOCONNECT;
+ goto cont_input;
+ }
+
+ if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) {
+ u_char code=ICMP_UNREACH_NET;
+ DEBUG_MISC((dfd," tcp fconnect errno = %d-%s\n",
+ errno,strerror(errno)));
+ if(errno == ECONNREFUSED) {
+ /* ACK the SYN, send RST to refuse the connection */
+ tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ } else {
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ HTONL(ti->ti_seq); /* restore tcp header */
+ HTONL(ti->ti_ack);
+ HTONS(ti->ti_win);
+ HTONS(ti->ti_urp);
+ m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ *ip=save_ip;
+ icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno));
+ }
+ tcp_close(tp);
+ m_free(m);
+ } else {
+ /*
+ * Haven't connected yet, save the current mbuf
+ * and ti, and return
+ * XXX Some OS's don't tell us whether the connect()
+ * succeeded or not. So we must time it out.
+ */
+ so->so_m = m;
+ so->so_ti = ti;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ }
+ return;
+
+ cont_conn:
+ /* m==NULL
+ * Check if the connect succeeded
+ */
+ if (so->so_state & SS_NOFDREF) {
+ tp = tcp_close(tp);
+ goto dropwithreset;
+ }
+ cont_input:
+ tcp_template(tp);
+
+ if (optp)
+ tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+
+ if (iss)
+ tp->iss = iss;
+ else
+ tp->iss = slirp->tcp_iss;
+ slirp->tcp_iss += TCP_ISSINCR/2;
+ tp->irs = ti->ti_seq;
+ tcp_sendseqinit(tp);
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ goto trimthenstep6;
+ } /* case TCPS_LISTEN */
+
+ /*
+ * If the state is SYN_SENT:
+ * if seg contains an ACK, but not for our SYN, drop the input.
+ * if seg contains a RST, then drop the connection.
+ * if seg does not contain SYN, then drop it.
+ * Otherwise this is an acceptable SYN segment
+ * initialize tp->rcv_nxt and tp->irs
+ * if seg contains ack then advance tp->snd_una
+ * if SYN has been acked change to ESTABLISHED else SYN_RCVD state
+ * arrange for segment to be acked (eventually)
+ * continue processing rest of data/controls, beginning with URG
+ */
+ case TCPS_SYN_SENT:
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LEQ(ti->ti_ack, tp->iss) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max)))
+ goto dropwithreset;
+
+ if (tiflags & TH_RST) {
+ if (tiflags & TH_ACK) {
+ tcp_drop(tp, 0); /* XXX Check t_softerror! */
+ }
+ goto drop;
+ }
+
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+ if (tiflags & TH_ACK) {
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+ }
+
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->irs = ti->ti_seq;
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
+ soisfconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+
+ (void) tcp_reass(tp, (struct tcpiphdr *)0,
+ (struct mbuf *)0);
+ /*
+ * if we didn't have to retransmit the SYN,
+ * use its rtt as our initial srtt & rtt var.
+ */
+ if (tp->t_rtt)
+ tcp_xmit_timer(tp, tp->t_rtt);
+ } else
+ tp->t_state = TCPS_SYN_RECEIVED;
+
+trimthenstep6:
+ /*
+ * Advance ti->ti_seq to correspond to first data byte.
+ * If data, trim to stay within window,
+ * dropping FIN if necessary.
+ */
+ ti->ti_seq++;
+ if (ti->ti_len > tp->rcv_wnd) {
+ todrop = ti->ti_len - tp->rcv_wnd;
+ m_adj(m, -todrop);
+ ti->ti_len = tp->rcv_wnd;
+ tiflags &= ~TH_FIN;
+ }
+ tp->snd_wl1 = ti->ti_seq - 1;
+ tp->rcv_up = ti->ti_seq;
+ goto step6;
+ } /* switch tp->t_state */
+ /*
+ * States other than LISTEN or SYN_SENT.
+ * Check that at least some bytes of segment are within
+ * receive window. If segment begins before rcv_nxt,
+ * drop leading data (and SYN); if nothing left, just ack.
+ */
+ todrop = tp->rcv_nxt - ti->ti_seq;
+ if (todrop > 0) {
+ if (tiflags & TH_SYN) {
+ tiflags &= ~TH_SYN;
+ ti->ti_seq++;
+ if (ti->ti_urp > 1)
+ ti->ti_urp--;
+ else
+ tiflags &= ~TH_URG;
+ todrop--;
+ }
+ /*
+ * Following if statement from Stevens, vol. 2, p. 960.
+ */
+ if (todrop > ti->ti_len
+ || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) {
+ /*
+ * Any valid FIN must be to the left of the window.
+ * At this point the FIN must be a duplicate or out
+ * of sequence; drop it.
+ */
+ tiflags &= ~TH_FIN;
+
+ /*
+ * Send an ACK to resynchronize and drop any data.
+ * But keep on processing for RST or ACK.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ todrop = ti->ti_len;
+ }
+ m_adj(m, todrop);
+ ti->ti_seq += todrop;
+ ti->ti_len -= todrop;
+ if (ti->ti_urp > todrop)
+ ti->ti_urp -= todrop;
+ else {
+ tiflags &= ~TH_URG;
+ ti->ti_urp = 0;
+ }
+ }
+ /*
+ * If new data are received on a connection after the
+ * user processes are gone, then RST the other end.
+ */
+ if ((so->so_state & SS_NOFDREF) &&
+ tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
+ tp = tcp_close(tp);
+ goto dropwithreset;
+ }
+
+ /*
+ * If segment ends after window, drop trailing data
+ * (and PUSH and FIN); if nothing left, just ACK.
+ */
+ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
+ if (todrop > 0) {
+ if (todrop >= ti->ti_len) {
+ /*
+ * If a new connection request is received
+ * while in TIME_WAIT, drop the old connection
+ * and start over if the sequence numbers
+ * are above the previous ones.
+ */
+ if (tiflags & TH_SYN &&
+ tp->t_state == TCPS_TIME_WAIT &&
+ SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
+ iss = tp->rcv_nxt + TCP_ISSINCR;
+ tp = tcp_close(tp);
+ goto findso;
+ }
+ /*
+ * If window is closed can only take segments at
+ * window edge, and have to drop data and PUSH from
+ * incoming segments. Continue processing, but
+ * remember to ack. Otherwise, drop segment
+ * and ack.
+ */
+ if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) {
+ tp->t_flags |= TF_ACKNOW;
+ } else {
+ goto dropafterack;
+ }
+ }
+ m_adj(m, -todrop);
+ ti->ti_len -= todrop;
+ tiflags &= ~(TH_PUSH|TH_FIN);
+ }
+
+ /*
+ * If the RST bit is set examine the state:
+ * SYN_RECEIVED STATE:
+ * If passive open, return to LISTEN state.
+ * If active open, inform user that connection was refused.
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
+ * Inform user that connection was reset, and close tcb.
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
+ * Close the tcb.
+ */
+ if (tiflags&TH_RST) switch (tp->t_state) {
+
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ tp->t_state = TCPS_CLOSED;
+ tcp_close(tp);
+ goto drop;
+
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+ tcp_close(tp);
+ goto drop;
+ }
+
+ /*
+ * If a SYN is in the window, then this is an
+ * error and we send an RST and drop the connection.
+ */
+ if (tiflags & TH_SYN) {
+ tp = tcp_drop(tp,0);
+ goto dropwithreset;
+ }
+
+ /*
+ * If the ACK bit is off we drop the segment and return.
+ */
+ if ((tiflags & TH_ACK) == 0) goto drop;
+
+ /*
+ * Ack processing.
+ */
+ switch (tp->t_state) {
+ /*
+ * In SYN_RECEIVED state if the ack ACKs our SYN then enter
+ * ESTABLISHED state and continue processing, otherwise
+ * send an RST. una<=ack<=max
+ */
+ case TCPS_SYN_RECEIVED:
+
+ if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max))
+ goto dropwithreset;
+ tp->t_state = TCPS_ESTABLISHED;
+ /*
+ * The sent SYN is ack'ed with our sequence number +1
+ * The first data byte already in the buffer will get
+ * lost if no correction is made. This is only needed for
+ * SS_CTL since the buffer is empty otherwise.
+ * tp->snd_una++; or:
+ */
+ tp->snd_una=ti->ti_ack;
+ if (so->so_state & SS_CTL) {
+ /* So tcp_ctl reports the right state */
+ ret = tcp_ctl(so);
+ if (ret == 1) {
+ soisfconnected(so);
+ so->so_state &= ~SS_CTL; /* success XXX */
+ } else if (ret == 2) {
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF; /* CTL_CMD */
+ } else {
+ needoutput = 1;
+ tp->t_state = TCPS_FIN_WAIT_1;
+ }
+ } else {
+ soisfconnected(so);
+ }
+
+ (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
+ tp->snd_wl1 = ti->ti_seq - 1;
+ /* Avoid ack processing; snd_una==ti_ack => dup ack */
+ goto synrx_to_est;
+ /* fall into ... */
+
+ /*
+ * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
+ * ACKs. If the ack is in the range
+ * tp->snd_una < ti->ti_ack <= tp->snd_max
+ * then advance tp->snd_una to ti->ti_ack and drop
+ * data from the retransmission queue. If this ACK reflects
+ * more up to date window information we update our window information.
+ */
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+
+ if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
+ if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
+ DEBUG_MISC((dfd," dup ack m = %lx so = %lx \n",
+ (long )m, (long )so));
+ /*
+ * If we have outstanding data (other than
+ * a window probe), this is a completely
+ * duplicate ack (ie, window info didn't
+ * change), the ack is the biggest we've
+ * seen and we've seen exactly our rexmt
+ * threshold of them, assume a packet
+ * has been dropped and retransmit it.
+ * Kludge snd_nxt & the congestion
+ * window so we send only this one
+ * packet.
+ *
+ * We know we're losing at the current
+ * window size so do congestion avoidance
+ * (set ssthresh to half the current window
+ * and pull our congestion window back to
+ * the new ssthresh).
+ *
+ * Dup acks mean that packets have left the
+ * network (they're now cached at the receiver)
+ * so bump cwnd by the amount in the receiver
+ * to keep a constant cwnd packets in the
+ * network.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 ||
+ ti->ti_ack != tp->snd_una)
+ tp->t_dupacks = 0;
+ else if (++tp->t_dupacks == TCPREXMTTHRESH) {
+ tcp_seq onxt = tp->snd_nxt;
+ u_int win =
+ min(tp->snd_wnd, tp->snd_cwnd) / 2 /
+ tp->t_maxseg;
+
+ if (win < 2)
+ win = 2;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->t_rtt = 0;
+ tp->snd_nxt = ti->ti_ack;
+ tp->snd_cwnd = tp->t_maxseg;
+ (void) tcp_output(tp);
+ tp->snd_cwnd = tp->snd_ssthresh +
+ tp->t_maxseg * tp->t_dupacks;
+ if (SEQ_GT(onxt, tp->snd_nxt))
+ tp->snd_nxt = onxt;
+ goto drop;
+ } else if (tp->t_dupacks > TCPREXMTTHRESH) {
+ tp->snd_cwnd += tp->t_maxseg;
+ (void) tcp_output(tp);
+ goto drop;
+ }
+ } else
+ tp->t_dupacks = 0;
+ break;
+ }
+ synrx_to_est:
+ /*
+ * If the congestion window was inflated to account
+ * for the other side's cached packets, retract it.
+ */
+ if (tp->t_dupacks > TCPREXMTTHRESH &&
+ tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->t_dupacks = 0;
+ if (SEQ_GT(ti->ti_ack, tp->snd_max)) {
+ goto dropafterack;
+ }
+ acked = ti->ti_ack - tp->snd_una;
+
+ /*
+ * If transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * Since we now have an rtt measurement, cancel the
+ * timer backoff (cf., Phil Karn's retransmit alg.).
+ * Recompute the initial retransmit timer.
+ */
+ if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp,tp->t_rtt);
+
+ /*
+ * If all outstanding data is acked, stop retransmit
+ * timer and remember to restart (more output or persist).
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value.
+ */
+ if (ti->ti_ack == tp->snd_max) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ needoutput = 1;
+ } else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg per packet).
+ * Otherwise open linearly: maxseg per window
+ * (maxseg^2 / cwnd per packet).
+ */
+ {
+ register u_int cw = tp->snd_cwnd;
+ register u_int incr = tp->t_maxseg;
+
+ if (cw > tp->snd_ssthresh)
+ incr = incr * incr / cw;
+ tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
+ }
+ if (acked > so->so_snd.sb_cc) {
+ tp->snd_wnd -= so->so_snd.sb_cc;
+ sbdrop(&so->so_snd, (int )so->so_snd.sb_cc);
+ ourfinisacked = 1;
+ } else {
+ sbdrop(&so->so_snd, acked);
+ tp->snd_wnd -= acked;
+ ourfinisacked = 0;
+ }
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+
+ switch (tp->t_state) {
+
+ /*
+ * In FIN_WAIT_1 STATE in addition to the processing
+ * for the ESTABLISHED state if our FIN is now acknowledged
+ * then enter FIN_WAIT_2.
+ */
+ case TCPS_FIN_WAIT_1:
+ if (ourfinisacked) {
+ /*
+ * If we can't receive any more
+ * data, then closing user can proceed.
+ * Starting the timer is contrary to the
+ * specification, but if we don't get a FIN
+ * we'll hang forever.
+ */
+ if (so->so_state & SS_FCANTRCVMORE) {
+ tp->t_timer[TCPT_2MSL] = TCP_MAXIDLE;
+ }
+ tp->t_state = TCPS_FIN_WAIT_2;
+ }
+ break;
+
+ /*
+ * In CLOSING STATE in addition to the processing for
+ * the ESTABLISHED state if the ACK acknowledges our FIN
+ * then enter the TIME-WAIT state, otherwise ignore
+ * the segment.
+ */
+ case TCPS_CLOSING:
+ if (ourfinisacked) {
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ }
+ break;
+
+ /*
+ * In LAST_ACK, we may still be waiting for data to drain
+ * and/or to be acked, as well as for the ack of our FIN.
+ * If our FIN is now acknowledged, delete the TCB,
+ * enter the closed state and return.
+ */
+ case TCPS_LAST_ACK:
+ if (ourfinisacked) {
+ tcp_close(tp);
+ goto drop;
+ }
+ break;
+
+ /*
+ * In TIME_WAIT state the only thing that should arrive
+ * is a retransmission of the remote FIN. Acknowledge
+ * it and restart the finack timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ goto dropafterack;
+ }
+ } /* switch(tp->t_state) */
+
+step6:
+ /*
+ * Update window information.
+ * Don't look at window if no ACK: TAC's send garbage on first SYN.
+ */
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LT(tp->snd_wl1, ti->ti_seq) ||
+ (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
+ (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))) {
+ tp->snd_wnd = tiwin;
+ tp->snd_wl1 = ti->ti_seq;
+ tp->snd_wl2 = ti->ti_ack;
+ if (tp->snd_wnd > tp->max_sndwnd)
+ tp->max_sndwnd = tp->snd_wnd;
+ needoutput = 1;
+ }
+
+ /*
+ * Process segments with URG.
+ */
+ if ((tiflags & TH_URG) && ti->ti_urp &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * This is a kludge, but if we receive and accept
+ * random urgent pointers, we'll crash in
+ * soreceive. It's hard to imagine someone
+ * actually wanting to send this much urgent data.
+ */
+ if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen) {
+ ti->ti_urp = 0;
+ tiflags &= ~TH_URG;
+ goto dodata;
+ }
+ /*
+ * If this segment advances the known urgent pointer,
+ * then mark the data stream. This should not happen
+ * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
+ * a FIN has been received from the remote side.
+ * In these states we ignore the URG.
+ *
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section as the original
+ * spec states (in one of two places).
+ */
+ if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+ so->so_urgc = so->so_rcv.sb_cc +
+ (tp->rcv_up - tp->rcv_nxt); /* -1; */
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+
+ }
+ } else
+ /*
+ * If no out of band data is expected,
+ * pull receive urgent pointer along
+ * with the receive window.
+ */
+ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
+ tp->rcv_up = tp->rcv_nxt;
+dodata:
+
+ /*
+ * Process the segment text, merging it into the TCP sequencing queue,
+ * and arranging for acknowledgment of receipt if necessary.
+ * This process logically involves adjusting tp->rcv_wnd as data
+ * is presented to the user (this happens in tcp_usrreq.c,
+ * case PRU_RCVD). If a FIN has already been received on this
+ * connection then we just ignore the text.
+ */
+ if ((ti->ti_len || (tiflags&TH_FIN)) &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ TCP_REASS(tp, ti, m, so, tiflags);
+ } else {
+ m_free(m);
+ tiflags &= ~TH_FIN;
+ }
+
+ /*
+ * If FIN is received ACK the FIN and let the user know
+ * that the connection is closing.
+ */
+ if (tiflags & TH_FIN) {
+ if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * If we receive a FIN we can't send more data,
+ * set it SS_FDRAIN
+ * Shutdown the socket if there is no rx data in the
+ * buffer.
+ * soread() is called on completion of shutdown() and
+ * will got to TCPS_LAST_ACK, and use tcp_output()
+ * to send the FIN.
+ */
+ sofwdrain(so);
+
+ tp->t_flags |= TF_ACKNOW;
+ tp->rcv_nxt++;
+ }
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED and ESTABLISHED STATES
+ * enter the CLOSE_WAIT state.
+ */
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ if(so->so_emu == EMU_CTL) /* no shutdown on socket */
+ tp->t_state = TCPS_LAST_ACK;
+ else
+ tp->t_state = TCPS_CLOSE_WAIT;
+ break;
+
+ /*
+ * If still in FIN_WAIT_1 STATE FIN has not been acked so
+ * enter the CLOSING state.
+ */
+ case TCPS_FIN_WAIT_1:
+ tp->t_state = TCPS_CLOSING;
+ break;
+
+ /*
+ * In FIN_WAIT_2 state enter the TIME_WAIT state,
+ * starting the time-wait timer, turning off the other
+ * standard timers.
+ */
+ case TCPS_FIN_WAIT_2:
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+
+ /*
+ * In TIME_WAIT state restart the 2 MSL time_wait timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+ }
+ }
+
+ /*
+ * If this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * See above.
+ */
+ if (ti->ti_len && (unsigned)ti->ti_len <= 5 &&
+ ((struct tcpiphdr_2 *)ti)->first_char == (char)27) {
+ tp->t_flags |= TF_ACKNOW;
+ }
+
+ /*
+ * Return any desired output.
+ */
+ if (needoutput || (tp->t_flags & TF_ACKNOW)) {
+ (void) tcp_output(tp);
+ }
+ return;
+
+dropafterack:
+ /*
+ * Generate an ACK dropping incoming segment if it occupies
+ * sequence space, where the ACK reflects our state.
+ */
+ if (tiflags & TH_RST)
+ goto drop;
+ m_freem(m);
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(tp);
+ return;
+
+dropwithreset:
+ /* reuses m if m!=NULL, m_free() unnecessary */
+ if (tiflags & TH_ACK)
+ tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+ else {
+ if (tiflags & TH_SYN) ti->ti_len++;
+ tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ }
+
+ return;
+
+drop:
+ /*
+ * Drop space held by incoming segment and return.
+ */
+ m_free(m);
+
+ return;
+}
+
+static void
+tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti)
+{
+ uint16_t mss;
+ int opt, optlen;
+
+ DEBUG_CALL("tcp_dooptions");
+ DEBUG_ARGS((dfd," tp = %lx cnt=%i \n", (long )tp, cnt));
+
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[0];
+ if (opt == TCPOPT_EOL)
+ break;
+ if (opt == TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[1];
+ if (optlen <= 0)
+ break;
+ }
+ switch (opt) {
+
+ default:
+ continue;
+
+ case TCPOPT_MAXSEG:
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ memcpy((char *) &mss, (char *) cp + 2, sizeof(mss));
+ NTOHS(mss);
+ (void) tcp_mss(tp, mss); /* sets t_maxseg */
+ break;
+ }
+ }
+}
+
+
+/*
+ * Pull out of band byte out of a segment so
+ * it doesn't appear in the user's data queue.
+ * It is still reflected in the segment length for
+ * sequencing purposes.
+ */
+
+#ifdef notdef
+
+void
+tcp_pulloutofband(so, ti, m)
+ struct socket *so;
+ struct tcpiphdr *ti;
+ register struct mbuf *m;
+{
+ int cnt = ti->ti_urp - 1;
+
+ while (cnt >= 0) {
+ if (m->m_len > cnt) {
+ char *cp = mtod(m, caddr_t) + cnt;
+ struct tcpcb *tp = sototcpcb(so);
+
+ tp->t_iobc = *cp;
+ tp->t_oobflags |= TCPOOB_HAVEDATA;
+ memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1));
+ m->m_len--;
+ return;
+ }
+ cnt -= m->m_len;
+ m = m->m_next; /* XXX WRONG! Fix it! */
+ if (m == 0)
+ break;
+ }
+ panic("tcp_pulloutofband");
+}
+
+#endif /* notdef */
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+
+static void
+tcp_xmit_timer(register struct tcpcb *tp, int rtt)
+{
+ register short delta;
+
+ DEBUG_CALL("tcp_xmit_timer");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("rtt = %d", rtt);
+
+ if (tp->t_srtt != 0) {
+ /*
+ * srtt is stored as fixed point with 3 bits after the
+ * binary point (i.e., scaled by 8). The following magic
+ * is equivalent to the smoothing algorithm in rfc793 with
+ * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
+ * point). Adjust rtt to origin 0.
+ */
+ delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);
+ if ((tp->t_srtt += delta) <= 0)
+ tp->t_srtt = 1;
+ /*
+ * We accumulate a smoothed rtt variance (actually, a
+ * smoothed mean difference), then set the retransmit
+ * timer to smoothed rtt + 4 times the smoothed variance.
+ * rttvar is stored as fixed point with 2 bits after the
+ * binary point (scaled by 4). The following is
+ * equivalent to rfc793 smoothing with an alpha of .75
+ * (rttvar = rttvar*3/4 + |delta| / 4). This replaces
+ * rfc793's wired-in beta.
+ */
+ if (delta < 0)
+ delta = -delta;
+ delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+ if ((tp->t_rttvar += delta) <= 0)
+ tp->t_rttvar = 1;
+ } else {
+ /*
+ * No rtt measurement yet - use the unsmoothed rtt.
+ * Set the variance to half the rtt (so our first
+ * retransmit happens at 3*rtt).
+ */
+ tp->t_srtt = rtt << TCP_RTT_SHIFT;
+ tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
+ }
+ tp->t_rtt = 0;
+ tp->t_rxtshift = 0;
+
+ /*
+ * the retransmit should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ */
+ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+
+ /*
+ * We received an ack for a packet that wasn't retransmitted;
+ * it is probably safe to discard any error indications we've
+ * received recently. This isn't quite right, but close enough
+ * for now (a route might have failed after we sent a segment,
+ * and the return path might not be symmetrical).
+ */
+ tp->t_softerror = 0;
+}
+
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * If none, use an mss that can be handled on the outgoing
+ * interface without forcing IP to fragment; if bigger than
+ * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
+ * to utilize large mbufs. If no route is found, route has no mtu,
+ * or the destination isn't local, use a default, hopefully conservative
+ * size (usually 512 or the default IP max size, but no more than the mtu
+ * of the interface), as we can't discover anything about intervening
+ * gateways or networks. We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+
+int
+tcp_mss(struct tcpcb *tp, u_int offer)
+{
+ struct socket *so = tp->t_socket;
+ int mss;
+
+ DEBUG_CALL("tcp_mss");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("offer = %d", offer);
+
+ mss = min(IF_MTU, IF_MRU) - sizeof(struct tcpiphdr);
+ if (offer)
+ mss = min(mss, offer);
+ mss = max(mss, 32);
+ if (mss < tp->t_maxseg || offer != 0)
+ tp->t_maxseg = mss;
+
+ tp->snd_cwnd = mss;
+
+ sbreserve(&so->so_snd, TCP_SNDSPACE + ((TCP_SNDSPACE % mss) ?
+ (mss - (TCP_SNDSPACE % mss)) :
+ 0));
+ sbreserve(&so->so_rcv, TCP_RCVSPACE + ((TCP_RCVSPACE % mss) ?
+ (mss - (TCP_RCVSPACE % mss)) :
+ 0));
+
+ DEBUG_MISC((dfd, " returning mss = %d\n", mss));
+
+ return mss;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_output.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_output.c
new file mode 100644
index 0000000..01725db
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_output.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93
+ * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+
+static const u_char tcp_outflags[TCP_NSTATES] = {
+ TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
+ TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK,
+ TH_FIN|TH_ACK, TH_ACK, TH_ACK,
+};
+
+
+#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */
+
+/*
+ * Tcp output routine: figure out what should be sent and send it.
+ */
+int
+tcp_output(struct tcpcb *tp)
+{
+ register struct socket *so = tp->t_socket;
+ register long len, win;
+ int off, flags, error;
+ register struct mbuf *m;
+ register struct tcpiphdr *ti;
+ u_char opt[MAX_TCPOPTLEN];
+ unsigned optlen, hdrlen;
+ int idle, sendalot;
+
+ DEBUG_CALL("tcp_output");
+ DEBUG_ARG("tp = %lx", (long )tp);
+
+ /*
+ * Determine length of data that should be transmitted,
+ * and flags that will be used.
+ * If there is some data or critical controls (SYN, RST)
+ * to send, then transmit; otherwise, investigate further.
+ */
+ idle = (tp->snd_max == tp->snd_una);
+ if (idle && tp->t_idle >= tp->t_rxtcur)
+ /*
+ * We have been idle for "a while" and no acks are
+ * expected to clock out any data we send --
+ * slow start to get ack "clock" running again.
+ */
+ tp->snd_cwnd = tp->t_maxseg;
+again:
+ sendalot = 0;
+ off = tp->snd_nxt - tp->snd_una;
+ win = min(tp->snd_wnd, tp->snd_cwnd);
+
+ flags = tcp_outflags[tp->t_state];
+
+ DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags));
+
+ /*
+ * If in persist timeout with window of 0, send 1 byte.
+ * Otherwise, if window is small but nonzero
+ * and timer expired, we will send what we can
+ * and go to transmit state.
+ */
+ if (tp->t_force) {
+ if (win == 0) {
+ /*
+ * If we still have some data to send, then
+ * clear the FIN bit. Usually this would
+ * happen below when it realizes that we
+ * aren't sending all the data. However,
+ * if we have exactly 1 byte of unset data,
+ * then it won't clear the FIN bit below,
+ * and if we are in persist state, we wind
+ * up sending the packet without recording
+ * that we sent the FIN bit.
+ *
+ * We can't just blindly clear the FIN bit,
+ * because if we don't have any more data
+ * to send then the probe will be the FIN
+ * itself.
+ */
+ if (off < so->so_snd.sb_cc)
+ flags &= ~TH_FIN;
+ win = 1;
+ } else {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+
+ len = min(so->so_snd.sb_cc, win) - off;
+
+ if (len < 0) {
+ /*
+ * If FIN has been sent but not acked,
+ * but we haven't been called to retransmit,
+ * len will be -1. Otherwise, window shrank
+ * after we sent into it. If window shrank to 0,
+ * cancel pending retransmit and pull snd_nxt
+ * back to (closed) window. We will enter persist
+ * state below. If the window didn't close completely,
+ * just wait for an ACK.
+ */
+ len = 0;
+ if (win == 0) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->snd_nxt = tp->snd_una;
+ }
+ }
+
+ if (len > tp->t_maxseg) {
+ len = tp->t_maxseg;
+ sendalot = 1;
+ }
+ if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
+ flags &= ~TH_FIN;
+
+ win = sbspace(&so->so_rcv);
+
+ /*
+ * Sender silly window avoidance. If connection is idle
+ * and can send all data, a maximum segment,
+ * at least a maximum default-size segment do it,
+ * or are forced, do it; otherwise don't bother.
+ * If peer's buffer is tiny, then send
+ * when window is at least half open.
+ * If retransmitting (possibly after persist timer forced us
+ * to send into a small window), then must resend.
+ */
+ if (len) {
+ if (len == tp->t_maxseg)
+ goto send;
+ if ((1 || idle || tp->t_flags & TF_NODELAY) &&
+ len + off >= so->so_snd.sb_cc)
+ goto send;
+ if (tp->t_force)
+ goto send;
+ if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
+ goto send;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+ goto send;
+ }
+
+ /*
+ * Compare available window to amount of window
+ * known to peer (as advertised window less
+ * next expected input). If the difference is at least two
+ * max size segments, or at least 50% of the maximum possible
+ * window, then want to send a window update to peer.
+ */
+ if (win > 0) {
+ /*
+ * "adv" is the amount we can increase the window,
+ * taking into account that we are limited by
+ * TCP_MAXWIN << tp->rcv_scale.
+ */
+ long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
+ (tp->rcv_adv - tp->rcv_nxt);
+
+ if (adv >= (long) (2 * tp->t_maxseg))
+ goto send;
+ if (2 * adv >= (long) so->so_rcv.sb_datalen)
+ goto send;
+ }
+
+ /*
+ * Send if we owe peer an ACK.
+ */
+ if (tp->t_flags & TF_ACKNOW)
+ goto send;
+ if (flags & (TH_SYN|TH_RST))
+ goto send;
+ if (SEQ_GT(tp->snd_up, tp->snd_una))
+ goto send;
+ /*
+ * If our state indicates that FIN should be sent
+ * and we have not yet done so, or we're retransmitting the FIN,
+ * then we need to send.
+ */
+ if (flags & TH_FIN &&
+ ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
+ goto send;
+
+ /*
+ * TCP window updates are not reliable, rather a polling protocol
+ * using ``persist'' packets is used to insure receipt of window
+ * updates. The three ``states'' for the output side are:
+ * idle not doing retransmits or persists
+ * persisting to move a small or zero window
+ * (re)transmitting and thereby not persisting
+ *
+ * tp->t_timer[TCPT_PERSIST]
+ * is set when we are in persist state.
+ * tp->t_force
+ * is set when we are called to send a persist packet.
+ * tp->t_timer[TCPT_REXMT]
+ * is set when we are retransmitting
+ * The output side is idle when both timers are zero.
+ *
+ * If send window is too small, there is data to transmit, and no
+ * retransmit or persist is pending, then go to persist state.
+ * If nothing happens soon, send when timer expires:
+ * if window is nonzero, transmit what we can,
+ * otherwise force out a byte.
+ */
+ if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->t_timer[TCPT_PERSIST] == 0) {
+ tp->t_rxtshift = 0;
+ tcp_setpersist(tp);
+ }
+
+ /*
+ * No reason to send a segment, just return.
+ */
+ return (0);
+
+send:
+ /*
+ * Before ESTABLISHED, force sending of initial options
+ * unless TCP set not to do any options.
+ * NOTE: we assume that the IP/TCP header plus TCP options
+ * always fit in a single mbuf, leaving room for a maximum
+ * link header, i.e.
+ * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
+ */
+ optlen = 0;
+ hdrlen = sizeof (struct tcpiphdr);
+ if (flags & TH_SYN) {
+ tp->snd_nxt = tp->iss;
+ if ((tp->t_flags & TF_NOOPT) == 0) {
+ uint16_t mss;
+
+ opt[0] = TCPOPT_MAXSEG;
+ opt[1] = 4;
+ mss = htons((uint16_t) tcp_mss(tp, 0));
+ memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
+ optlen = 4;
+ }
+ }
+
+ hdrlen += optlen;
+
+ /*
+ * Adjust data length if insertion of options will
+ * bump the packet length beyond the t_maxseg length.
+ */
+ if (len > tp->t_maxseg - optlen) {
+ len = tp->t_maxseg - optlen;
+ sendalot = 1;
+ }
+
+ /*
+ * Grab a header mbuf, attaching a copy of data to
+ * be transmitted, and initialize the header from
+ * the template for sends on this connection.
+ */
+ if (len) {
+ m = m_get(so->slirp);
+ if (m == NULL) {
+ error = 1;
+ goto out;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ m->m_len = hdrlen;
+
+ sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen);
+ m->m_len += len;
+
+ /*
+ * If we're sending everything we've got, set PUSH.
+ * (This will keep happy those implementations which only
+ * give data to the user when a buffer fills or
+ * a PUSH comes in.)
+ */
+ if (off + len == so->so_snd.sb_cc)
+ flags |= TH_PUSH;
+ } else {
+ m = m_get(so->slirp);
+ if (m == NULL) {
+ error = 1;
+ goto out;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ m->m_len = hdrlen;
+ }
+
+ ti = mtod(m, struct tcpiphdr *);
+
+ memcpy((caddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr));
+
+ /*
+ * Fill in fields, remembering maximum advertised
+ * window for use in delaying messages about window sizes.
+ * If resending a FIN, be sure not to use a new sequence number.
+ */
+ if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
+ tp->snd_nxt == tp->snd_max)
+ tp->snd_nxt--;
+ /*
+ * If we are doing retransmissions, then snd_nxt will
+ * not reflect the first unsent octet. For ACK only
+ * packets, we do not want the sequence number of the
+ * retransmitted packet, we want the sequence number
+ * of the next unsent octet. So, if there is no data
+ * (and no SYN or FIN), use snd_max instead of snd_nxt
+ * when filling in ti_seq. But if we are in persist
+ * state, snd_max might reflect one byte beyond the
+ * right edge of the window, so use snd_nxt in that
+ * case, since we know we aren't doing a retransmission.
+ * (retransmit and persist are mutually exclusive...)
+ */
+ if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+ ti->ti_seq = htonl(tp->snd_nxt);
+ else
+ ti->ti_seq = htonl(tp->snd_max);
+ ti->ti_ack = htonl(tp->rcv_nxt);
+ if (optlen) {
+ memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen);
+ ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
+ }
+ ti->ti_flags = flags;
+ /*
+ * Calculate receive window. Don't shrink window,
+ * but avoid silly window syndrome.
+ */
+ if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg)
+ win = 0;
+ if (win > (long)TCP_MAXWIN << tp->rcv_scale)
+ win = (long)TCP_MAXWIN << tp->rcv_scale;
+ if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
+ win = (long)(tp->rcv_adv - tp->rcv_nxt);
+ ti->ti_win = htons((uint16_t) (win>>tp->rcv_scale));
+
+ if (SEQ_GT(tp->snd_up, tp->snd_una)) {
+ ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq)));
+ ti->ti_flags |= TH_URG;
+ } else
+ /*
+ * If no urgent pointer to send, then we pull
+ * the urgent pointer to the left edge of the send window
+ * so that it doesn't drift into the send window on sequence
+ * number wraparound.
+ */
+ tp->snd_up = tp->snd_una; /* drag it along */
+
+ /*
+ * Put TCP length in extended header, and then
+ * checksum extended header and data.
+ */
+ if (len + optlen)
+ ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) +
+ optlen + len));
+ ti->ti_sum = cksum(m, (int)(hdrlen + len));
+
+ /*
+ * In transmit state, time the transmission and arrange for
+ * the retransmit. In persist state, just set snd_max.
+ */
+ if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
+ tcp_seq startseq = tp->snd_nxt;
+
+ /*
+ * Advance snd_nxt over sequence space of this segment.
+ */
+ if (flags & (TH_SYN|TH_FIN)) {
+ if (flags & TH_SYN)
+ tp->snd_nxt++;
+ if (flags & TH_FIN) {
+ tp->snd_nxt++;
+ tp->t_flags |= TF_SENTFIN;
+ }
+ }
+ tp->snd_nxt += len;
+ if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
+ tp->snd_max = tp->snd_nxt;
+ /*
+ * Time this transmission if not a retransmission and
+ * not currently timing anything.
+ */
+ if (tp->t_rtt == 0) {
+ tp->t_rtt = 1;
+ tp->t_rtseq = startseq;
+ }
+ }
+
+ /*
+ * Set retransmit timer if not currently set,
+ * and not doing an ack or a keep-alive probe.
+ * Initial value for retransmit timer is smoothed
+ * round-trip time + 2 * round-trip time variance.
+ * Initialize shift counter which is used for backoff
+ * of retransmit time.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->snd_nxt != tp->snd_una) {
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ if (tp->t_timer[TCPT_PERSIST]) {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+ } else
+ if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
+ tp->snd_max = tp->snd_nxt + len;
+
+ /*
+ * Fill in IP length and desired time to live and
+ * send to IP level. There should be a better way
+ * to handle ttl and tos; we could keep them in
+ * the template, but need a way to checksum without them.
+ */
+ m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
+
+ {
+
+ ((struct ip *)ti)->ip_len = m->m_len;
+
+ ((struct ip *)ti)->ip_ttl = IPDEFTTL;
+ ((struct ip *)ti)->ip_tos = so->so_iptos;
+
+ error = ip_output(so, m);
+ }
+ if (error) {
+out:
+ return (error);
+ }
+
+ /*
+ * Data sent (as far as we can tell).
+ * If this advertises a larger window than any other segment,
+ * then remember the size of the advertised window.
+ * Any pending ACK has now been sent.
+ */
+ if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
+ tp->rcv_adv = tp->rcv_nxt + win;
+ tp->last_ack_sent = tp->rcv_nxt;
+ tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+ if (sendalot)
+ goto again;
+
+ return (0);
+}
+
+void
+tcp_setpersist(struct tcpcb *tp)
+{
+ int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+
+ /*
+ * Start/restart persistence timer.
+ */
+ TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
+ t * tcp_backoff[tp->t_rxtshift],
+ TCPTV_PERSMIN, TCPTV_PERSMAX);
+ if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
+ tp->t_rxtshift++;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_subr.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_subr.c
new file mode 100644
index 0000000..1557e89
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_subr.c
@@ -0,0 +1,915 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93
+ * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+
+/* patchable/settable parameters for tcp */
+/* Don't do rfc1323 performance enhancements */
+#define TCP_DO_RFC1323 0
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init(Slirp *slirp)
+{
+ slirp->tcp_iss = 1; /* wrong */
+ slirp->tcb.so_next = slirp->tcb.so_prev = &slirp->tcb;
+ slirp->tcp_last_so = &slirp->tcb;
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+void
+tcp_template(struct tcpcb *tp)
+{
+ struct socket *so = tp->t_socket;
+ register struct tcpiphdr *n = &tp->t_template;
+
+ n->ti_mbuf = NULL;
+ n->ti_x1 = 0;
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
+ n->ti_src = so->so_faddr;
+ n->ti_dst = so->so_laddr;
+ n->ti_sport = so->so_fport;
+ n->ti_dport = so->so_lport;
+
+ n->ti_seq = 0;
+ n->ti_ack = 0;
+ n->ti_x2 = 0;
+ n->ti_off = 5;
+ n->ti_flags = 0;
+ n->ti_win = 0;
+ n->ti_sum = 0;
+ n->ti_urp = 0;
+}
+
+/*
+ * Send a single message to the TCP at address specified by
+ * the given TCP/IP header. If m == 0, then we make a copy
+ * of the tcpiphdr at ti and send directly to the addressed host.
+ * This is used to force keep alive messages out using the TCP
+ * template for a connection tp->t_template. If flags are given
+ * then we send a message back to the TCP which originated the
+ * segment ti, and discard the mbuf containing it and any other
+ * attached mbufs.
+ *
+ * In any case the ack and sequence number of the transmitted
+ * segment are as specified by the parameters.
+ */
+void
+tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
+ tcp_seq ack, tcp_seq seq, int flags)
+{
+ register int tlen;
+ int win = 0;
+
+ DEBUG_CALL("tcp_respond");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("ti = %lx", (long)ti);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("ack = %u", ack);
+ DEBUG_ARG("seq = %u", seq);
+ DEBUG_ARG("flags = %x", flags);
+
+ if (tp)
+ win = sbspace(&tp->t_socket->so_rcv);
+ if (m == NULL) {
+ if ((m = m_get(tp->t_socket->slirp)) == NULL)
+ return;
+ tlen = 0;
+ m->m_data += IF_MAXLINKHDR;
+ *mtod(m, struct tcpiphdr *) = *ti;
+ ti = mtod(m, struct tcpiphdr *);
+ flags = TH_ACK;
+ } else {
+ /*
+ * ti points into m so the next line is just making
+ * the mbuf point to ti
+ */
+ m->m_data = (caddr_t)ti;
+
+ m->m_len = sizeof (struct tcpiphdr);
+ tlen = 0;
+#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
+ xchg(ti->ti_dport, ti->ti_sport, uint16_t);
+#undef xchg
+ }
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
+ tlen += sizeof (struct tcpiphdr);
+ m->m_len = tlen;
+
+ ti->ti_mbuf = NULL;
+ ti->ti_x1 = 0;
+ ti->ti_seq = htonl(seq);
+ ti->ti_ack = htonl(ack);
+ ti->ti_x2 = 0;
+ ti->ti_off = sizeof (struct tcphdr) >> 2;
+ ti->ti_flags = flags;
+ if (tp)
+ ti->ti_win = htons((uint16_t) (win >> tp->rcv_scale));
+ else
+ ti->ti_win = htons((uint16_t)win);
+ ti->ti_urp = 0;
+ ti->ti_sum = 0;
+ ti->ti_sum = cksum(m, tlen);
+ ((struct ip *)ti)->ip_len = tlen;
+
+ if(flags & TH_RST)
+ ((struct ip *)ti)->ip_ttl = MAXTTL;
+ else
+ ((struct ip *)ti)->ip_ttl = IPDEFTTL;
+
+ (void) ip_output((struct socket *)0, m);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(struct socket *so)
+{
+ register struct tcpcb *tp;
+
+ tp = (struct tcpcb *)malloc(sizeof(*tp));
+ if (tp == NULL)
+ return ((struct tcpcb *)0);
+
+ memset((char *) tp, 0, sizeof(struct tcpcb));
+ tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
+ tp->t_maxseg = TCP_MSS;
+
+ tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+ tp->t_socket = so;
+
+ /*
+ * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
+ * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
+ * reasonable initial retransmit time.
+ */
+ tp->t_srtt = TCPTV_SRTTBASE;
+ tp->t_rttvar = TCPTV_SRTTDFLT << 2;
+ tp->t_rttmin = TCPTV_MIN;
+
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
+ TCPTV_MIN, TCPTV_REXMTMAX);
+
+ tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->t_state = TCPS_CLOSED;
+
+ so->so_tcpcb = tp;
+
+ return (tp);
+}
+
+/*
+ * Drop a TCP connection, reporting
+ * the specified error. If connection is synchronized,
+ * then send a RST to peer.
+ */
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err)
+{
+ DEBUG_CALL("tcp_drop");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("errno = %d", errno);
+
+ if (TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_state = TCPS_CLOSED;
+ (void) tcp_output(tp);
+ }
+ return (tcp_close(tp));
+}
+
+/*
+ * Close a TCP control block:
+ * discard all space held by the tcp
+ * discard internet protocol block
+ * wake up any sleepers
+ */
+struct tcpcb *
+tcp_close(struct tcpcb *tp)
+{
+ register struct tcpiphdr *t;
+ struct socket *so = tp->t_socket;
+ Slirp *slirp = so->slirp;
+ register struct mbuf *m;
+
+ DEBUG_CALL("tcp_close");
+ DEBUG_ARG("tp = %lx", (long )tp);
+
+ /* free the reassembly queue, if any */
+ t = tcpfrag_list_first(tp);
+ while (!tcpfrag_list_end(t, tp)) {
+ t = tcpiphdr_next(t);
+ m = tcpiphdr_prev(t)->ti_mbuf;
+ remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
+ m_freem(m);
+ }
+ free(tp);
+ so->so_tcpcb = NULL;
+ /* clobber input socket cache if we're closing the cached connection */
+ if (so == slirp->tcp_last_so)
+ slirp->tcp_last_so = &slirp->tcb;
+ closesocket(so->s);
+ sbfree(&so->so_rcv);
+ sbfree(&so->so_snd);
+ sofree(so);
+ return ((struct tcpcb *)0);
+}
+
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+
+/*
+ * User issued close, and wish to trail through shutdown states:
+ * if never received SYN, just forget it. If got a SYN from peer,
+ * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
+ * If already got a FIN from peer, then almost done; go to LAST_ACK
+ * state. In all other cases, have already sent FIN to peer (e.g.
+ * after PRU_SHUTDOWN), and just have to play tedious game waiting
+ * for peer to send FIN or not respond to keep-alives, etc.
+ * We can let the user exit from the close as soon as the FIN is acked.
+ */
+void
+tcp_sockclosed(struct tcpcb *tp)
+{
+
+ DEBUG_CALL("tcp_sockclosed");
+ DEBUG_ARG("tp = %lx", (long)tp);
+
+ switch (tp->t_state) {
+
+ case TCPS_CLOSED:
+ case TCPS_LISTEN:
+ case TCPS_SYN_SENT:
+ tp->t_state = TCPS_CLOSED;
+ tp = tcp_close(tp);
+ break;
+
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ tp->t_state = TCPS_FIN_WAIT_1;
+ break;
+
+ case TCPS_CLOSE_WAIT:
+ tp->t_state = TCPS_LAST_ACK;
+ break;
+ }
+ if (tp)
+ tcp_output(tp);
+}
+
+/*
+ * Connect to a host on the Internet
+ * Called by tcp_input
+ * Only do a connect, the tcp fields will be set in tcp_input
+ * return 0 if there's a result of the connect,
+ * else return -1 means we're still connecting
+ * The return value is almost always -1 since the socket is
+ * nonblocking. Connect returns after the SYN is sent, and does
+ * not wait for ACK+SYN.
+ */
+int tcp_fconnect(struct socket *so)
+{
+ Slirp *slirp = so->slirp;
+ int ret=0;
+
+ DEBUG_CALL("tcp_fconnect");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ if( (ret = so->s = os_socket(AF_INET,SOCK_STREAM,0)) >= 0) {
+ int opt, s=so->s;
+ struct sockaddr_in addr;
+
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt ));
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(opt ));
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ /* It's an alias */
+ if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
+ if (get_dns_addr(&addr.sin_addr) < 0)
+ addr.sin_addr = loopback_addr;
+ } else {
+ addr.sin_addr = loopback_addr;
+ }
+ } else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, "
+ "addr.sin_addr.s_addr=%.16s\n",
+ ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+ /* We don't care what port we get */
+ ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
+
+ /*
+ * If it's not in progress, it failed, so we just return 0,
+ * without clearing SS_NOFDREF
+ */
+ soisfconnecting(so);
+ }
+
+ return(ret);
+}
+
+/*
+ * Accept the socket and connect to the local-host
+ *
+ * We have a problem. The correct thing to do would be
+ * to first connect to the local-host, and only if the
+ * connection is accepted, then do an accept() here.
+ * But, a) we need to know who's trying to connect
+ * to the socket to be able to SYN the local-host, and
+ * b) we are already connected to the foreign host by
+ * the time it gets to accept(), so... We simply accept
+ * here and SYN the local-host.
+ */
+void
+tcp_connect(struct socket *inso)
+{
+ Slirp *slirp = inso->slirp;
+ struct socket *so;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct tcpcb *tp;
+ int s, opt;
+
+ DEBUG_CALL("tcp_connect");
+ DEBUG_ARG("inso = %lx", (long)inso);
+
+ /*
+ * If it's an SS_ACCEPTONCE socket, no need to socreate()
+ * another socket, just use the accept() socket.
+ */
+ if (inso->so_state & SS_FACCEPTONCE) {
+ /* FACCEPTONCE already have a tcpcb */
+ so = inso;
+ } else {
+ if ((so = socreate(slirp)) == NULL) {
+ /* If it failed, get rid of the pending connection */
+ closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen));
+ return;
+ }
+ if (tcp_attach(so) < 0) {
+ free(so); /* NOT sofree */
+ return;
+ }
+ so->so_laddr = inso->so_laddr;
+ so->so_lport = inso->so_lport;
+ }
+
+ (void) tcp_mss(sototcpcb(so), 0);
+
+ if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0) {
+ tcp_close(sototcpcb(so)); /* This will sofree() as well */
+ return;
+ }
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(int));
+
+ so->so_fport = addr.sin_port;
+ so->so_faddr = addr.sin_addr;
+ /* Translate connections from localhost to the real hostname */
+ if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = slirp->vhost_addr;
+
+ /* Close the accept() socket, set right state */
+ if (inso->so_state & SS_FACCEPTONCE) {
+ closesocket(so->s); /* If we only accept once, close the accept() socket */
+ so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
+ /* if it's not FACCEPTONCE, it's already NOFDREF */
+ }
+ so->s = s;
+ so->so_state |= SS_INCOMING;
+
+ so->so_iptos = tcp_tos(so);
+ tp = sototcpcb(so);
+
+ tcp_template(tp);
+
+ tp->t_state = TCPS_SYN_SENT;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->iss = slirp->tcp_iss;
+ slirp->tcp_iss += TCP_ISSINCR/2;
+ tcp_sendseqinit(tp);
+ tcp_output(tp);
+}
+
+/*
+ * Attach a TCPCB to a socket.
+ */
+int
+tcp_attach(struct socket *so)
+{
+ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL)
+ return -1;
+
+ insque(so, &so->slirp->tcb);
+
+ return 0;
+}
+
+/*
+ * Set the socket's type of service field
+ */
+static const struct tos_t tcptos[] = {
+ {0, 20, IPTOS_THROUGHPUT, 0}, /* ftp data */
+ {21, 21, IPTOS_LOWDELAY, EMU_FTP}, /* ftp control */
+ {0, 23, IPTOS_LOWDELAY, 0}, /* telnet */
+ {0, 80, IPTOS_THROUGHPUT, 0}, /* WWW */
+ {0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT}, /* rlogin */
+ {0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT}, /* shell */
+ {0, 544, IPTOS_LOWDELAY, EMU_KSH}, /* kshell */
+ {0, 543, IPTOS_LOWDELAY, 0}, /* klogin */
+ {0, 6667, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC */
+ {0, 6668, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC undernet */
+ {0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */
+ {0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */
+ {0, 0, 0, 0}
+};
+
+static struct emu_t *tcpemu = NULL;
+
+/*
+ * Return TOS according to the above table
+ */
+uint8_t
+tcp_tos(struct socket *so)
+{
+ int i = 0;
+ struct emu_t *emup;
+
+ while(tcptos[i].tos) {
+ if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) ||
+ (tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) {
+ so->so_emu = tcptos[i].emu;
+ return tcptos[i].tos;
+ }
+ i++;
+ }
+
+ /* Nope, lets see if there's a user-added one */
+ for (emup = tcpemu; emup; emup = emup->next) {
+ if ((emup->fport && (ntohs(so->so_fport) == emup->fport)) ||
+ (emup->lport && (ntohs(so->so_lport) == emup->lport))) {
+ so->so_emu = emup->emu;
+ return emup->tos;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Emulate programs that try and connect to us
+ * This includes ftp (the data connection is
+ * initiated by the server) and IRC (DCC CHAT and
+ * DCC SEND) for now
+ *
+ * NOTE: It's possible to crash SLiRP by sending it
+ * unstandard strings to emulate... if this is a problem,
+ * more checks are needed here
+ *
+ * XXX Assumes the whole command came in one packet
+ *
+ * XXX Some ftp clients will have their TOS set to
+ * LOWDELAY and so Nagel will kick in. Because of this,
+ * we'll get the first letter, followed by the rest, so
+ * we simply scan for ORT instead of PORT...
+ * DCC doesn't have this problem because there's other stuff
+ * in the packet before the DCC command.
+ *
+ * Return 1 if the mbuf m is still valid and should be
+ * sbappend()ed
+ *
+ * NOTE: if you return 0 you MUST m_free() the mbuf!
+ */
+int
+tcp_emu(struct socket *so, struct mbuf *m)
+{
+ Slirp *slirp = so->slirp;
+ u_int n1, n2, n3, n4, n5, n6;
+ char buff[257];
+ uint32_t laddr;
+ u_int lport;
+ char *bptr;
+
+ DEBUG_CALL("tcp_emu");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ switch(so->so_emu) {
+ int x, i;
+
+ case EMU_IDENT:
+ /*
+ * Identification protocol as per rfc-1413
+ */
+
+ {
+ struct socket *tmpso;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+ so_rcv->sb_wptr += m->m_len;
+ so_rcv->sb_rptr += m->m_len;
+ m->m_data[m->m_len] = 0; /* NULL terminate */
+ if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) {
+ if (sscanf(so_rcv->sb_data, "%u%*[ ,]%u", &n1, &n2) == 2) {
+ HTONS(n1);
+ HTONS(n2);
+ /* n2 is the one on our host */
+ for (tmpso = slirp->tcb.so_next;
+ tmpso != &slirp->tcb;
+ tmpso = tmpso->so_next) {
+ if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr &&
+ tmpso->so_lport == n2 &&
+ tmpso->so_faddr.s_addr == so->so_faddr.s_addr &&
+ tmpso->so_fport == n1) {
+ if (getsockname(tmpso->s,
+ (struct sockaddr *)&addr, &addrlen) == 0)
+ n2 = ntohs(addr.sin_port);
+ break;
+ }
+ }
+ }
+ so_rcv->sb_cc = snprintf(so_rcv->sb_data,
+ so_rcv->sb_datalen,
+ "%d,%d\r\n", n1, n2);
+ so_rcv->sb_rptr = so_rcv->sb_data;
+ so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc;
+ }
+ m_free(m);
+ return 0;
+ }
+
+ case EMU_FTP: /* ftp */
+ *(m->m_data+m->m_len) = 0; /* NUL terminate for strstr */
+ if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) {
+ /*
+ * Need to emulate the PORT command
+ */
+ x = sscanf(bptr, "ORT %u,%u,%u,%u,%u,%u\r\n%256[^\177]",
+ &n1, &n2, &n3, &n4, &n5, &n6, buff);
+ if (x < 6)
+ return 1;
+
+ laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
+ lport = htons((n5 << 8) | (n6));
+
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr,
+ lport, SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ n6 = ntohs(so->so_fport);
+
+ n5 = (n6 >> 8) & 0xff;
+ n6 &= 0xff;
+
+ laddr = ntohl(so->so_faddr.s_addr);
+
+ n1 = ((laddr >> 24) & 0xff);
+ n2 = ((laddr >> 16) & 0xff);
+ n3 = ((laddr >> 8) & 0xff);
+ n4 = (laddr & 0xff);
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
+ "ORT %d,%d,%d,%d,%d,%d\r\n%s",
+ n1, n2, n3, n4, n5, n6, x==7?buff:"");
+ return 1;
+ } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {
+ /*
+ * Need to emulate the PASV response
+ */
+ x = sscanf(bptr, "27 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n%256[^\177]",
+ &n1, &n2, &n3, &n4, &n5, &n6, buff);
+ if (x < 6)
+ return 1;
+
+ laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
+ lport = htons((n5 << 8) | (n6));
+
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr,
+ lport, SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ n6 = ntohs(so->so_fport);
+
+ n5 = (n6 >> 8) & 0xff;
+ n6 &= 0xff;
+
+ laddr = ntohl(so->so_faddr.s_addr);
+
+ n1 = ((laddr >> 24) & 0xff);
+ n2 = ((laddr >> 16) & 0xff);
+ n3 = ((laddr >> 8) & 0xff);
+ n4 = (laddr & 0xff);
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
+ "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
+ n1, n2, n3, n4, n5, n6, x==7?buff:"");
+
+ return 1;
+ }
+
+ return 1;
+
+ case EMU_KSH:
+ /*
+ * The kshell (Kerberos rsh) and shell services both pass
+ * a local port port number to carry signals to the server
+ * and stderr to the client. It is passed at the beginning
+ * of the connection as a NUL-terminated decimal ASCII string.
+ */
+ so->so_emu = 0;
+ for (lport = 0, i = 0; i < m->m_len-1; ++i) {
+ if (m->m_data[i] < '0' || m->m_data[i] > '9')
+ return 1; /* invalid number */
+ lport *= 10;
+ lport += m->m_data[i] - '0';
+ }
+ if (m->m_data[m->m_len-1] == '\0' && lport != 0 &&
+ (so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr,
+ htons(lport), SS_FACCEPTONCE)) != NULL)
+ m->m_len = snprintf(m->m_data, m->m_hdr.mh_size, "%d",
+ ntohs(so->so_fport)) + 1;
+ return 1;
+
+ case EMU_IRC:
+ /*
+ * Need to emulate DCC CHAT, DCC SEND and DCC MOVE
+ */
+ *(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */
+ if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL)
+ return 1;
+
+ /* The %256s is for the broken mIRC */
+ if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) {
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0,
+ htonl(laddr), htons(lport),
+ SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC CHAT chat %lu %u%c\n",
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), 1);
+ } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0,
+ htonl(laddr), htons(lport),
+ SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC SEND %s %lu %u %u%c\n", buff,
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), n1, 1);
+ } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0,
+ htonl(laddr), htons(lport),
+ SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC MOVE %s %lu %u %u%c\n", buff,
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), n1, 1);
+ }
+ return 1;
+
+ case EMU_REALAUDIO:
+ /*
+ * RealAudio emulation - JP. We must try to parse the incoming
+ * data and try to find the two characters that contain the
+ * port number. Then we redirect an udp port and replace the
+ * number with the real port we got.
+ *
+ * The 1.0 beta versions of the player are not supported
+ * any more.
+ *
+ * A typical packet for player version 1.0 (release version):
+ *
+ * 0000:50 4E 41 00 05
+ * 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 ........g.l.c..P
+ * 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH
+ * 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v
+ * 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB
+ *
+ * Now the port number 0x1BD7 is found at offset 0x04 of the
+ * Now the port number 0x1BD7 is found at offset 0x04 of the
+ * second packet. This time we received five bytes first and
+ * then the rest. You never know how many bytes you get.
+ *
+ * A typical packet for player version 2.0 (beta):
+ *
+ * 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA.............
+ * 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .gux.c..Win2.0.0
+ * 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/
+ * 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas
+ * 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B
+ *
+ * Port number 0x1BC1 is found at offset 0x0d.
+ *
+ * This is just a horrible switch statement. Variable ra tells
+ * us where we're going.
+ */
+
+ bptr = m->m_data;
+ while (bptr < m->m_data + m->m_len) {
+ u_short p;
+ static int ra = 0;
+ char ra_tbl[4];
+
+ ra_tbl[0] = 0x50;
+ ra_tbl[1] = 0x4e;
+ ra_tbl[2] = 0x41;
+ ra_tbl[3] = 0;
+
+ switch (ra) {
+ case 0:
+ case 2:
+ case 3:
+ if (*bptr++ != ra_tbl[ra]) {
+ ra = 0;
+ continue;
+ }
+ break;
+
+ case 1:
+ /*
+ * We may get 0x50 several times, ignore them
+ */
+ if (*bptr == 0x50) {
+ ra = 1;
+ bptr++;
+ continue;
+ } else if (*bptr++ != ra_tbl[ra]) {
+ ra = 0;
+ continue;
+ }
+ break;
+
+ case 4:
+ /*
+ * skip version number
+ */
+ bptr++;
+ break;
+
+ case 5:
+ /*
+ * The difference between versions 1.0 and
+ * 2.0 is here. For future versions of
+ * the player this may need to be modified.
+ */
+ if (*(bptr + 1) == 0x02)
+ bptr += 8;
+ else
+ bptr += 4;
+ break;
+
+ case 6:
+ /* This is the field containing the port
+ * number that RA-player is listening to.
+ */
+ lport = (((u_char*)bptr)[0] << 8)
+ + ((u_char *)bptr)[1];
+ if (lport < 6970)
+ lport += 256; /* don't know why */
+ if (lport < 6970 || lport > 7170)
+ return 1; /* failed */
+
+ /* try to get udp port between 6970 - 7170 */
+ for (p = 6970; p < 7071; p++) {
+ if (udp_listen(slirp, INADDR_ANY,
+ htons(p),
+ so->so_laddr.s_addr,
+ htons(lport),
+ SS_FACCEPTONCE)) {
+ break;
+ }
+ }
+ if (p == 7071)
+ p = 0;
+ *(u_char *)bptr++ = (p >> 8) & 0xff;
+ *(u_char *)bptr = p & 0xff;
+ ra = 0;
+ return 1; /* port redirected, we're done */
+ break;
+
+ default:
+ ra = 0;
+ }
+ ra++;
+ }
+ return 1;
+
+ default:
+ /* Ooops, not emulated, won't call tcp_emu again */
+ so->so_emu = 0;
+ return 1;
+ }
+}
+
+/*
+ * Do misc. config of SLiRP while its running.
+ * Return 0 if this connections is to be closed, 1 otherwise,
+ * return 2 if this is a command-line connection
+ */
+int tcp_ctl(struct socket *so)
+{
+ Slirp *slirp = so->slirp;
+ struct sbuf *sb = &so->so_snd;
+ struct ex_list *ex_ptr;
+ int do_pty;
+
+ DEBUG_CALL("tcp_ctl");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
+ /* Check if it's pty_exec */
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_fport == so->so_fport &&
+ so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
+ if (ex_ptr->ex_pty == 3) {
+ so->s = -1;
+ so->extra = (void *)ex_ptr->ex_exec;
+ return 1;
+ }
+ do_pty = ex_ptr->ex_pty;
+ DEBUG_MISC((dfd, " executing %s \n",ex_ptr->ex_exec));
+ return fork_exec(so, ex_ptr->ex_exec, do_pty);
+ }
+ }
+ }
+ sb->sb_cc =
+ snprintf(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data),
+ "Error: No application configured.\r\n");
+ sb->sb_wptr += sb->sb_cc;
+ return 0;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_timer.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_timer.c
new file mode 100644
index 0000000..1e6e051
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_timer.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93
+ * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
+ */
+
+#include "slirp.h"
+
+static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer);
+
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+void
+tcp_fasttimo(Slirp *slirp)
+{
+ register struct socket *so;
+ register struct tcpcb *tp;
+
+ DEBUG_CALL("tcp_fasttimo");
+
+ so = slirp->tcb.so_next;
+ if (so)
+ for (; so != &slirp->tcb; so = so->so_next)
+ if ((tp = (struct tcpcb *)so->so_tcpcb) &&
+ (tp->t_flags & TF_DELACK)) {
+ tp->t_flags &= ~TF_DELACK;
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(tp);
+ }
+}
+
+/*
+ * Tcp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active tcb's and
+ * causes finite state machine actions if timers expire.
+ */
+void
+tcp_slowtimo(Slirp *slirp)
+{
+ register struct socket *ip, *ipnxt;
+ register struct tcpcb *tp;
+ register int i;
+
+ DEBUG_CALL("tcp_slowtimo");
+
+ /*
+ * Search through tcb's and update active timers.
+ */
+ ip = slirp->tcb.so_next;
+ if (ip == NULL) {
+ return;
+ }
+ for (; ip != &slirp->tcb; ip = ipnxt) {
+ ipnxt = ip->so_next;
+ tp = sototcpcb(ip);
+ if (tp == NULL) {
+ continue;
+ }
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
+ tcp_timers(tp,i);
+ if (ipnxt->so_prev != ip)
+ goto tpgone;
+ }
+ }
+ tp->t_idle++;
+ if (tp->t_rtt)
+ tp->t_rtt++;
+tpgone:
+ ;
+ }
+ slirp->tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
+ slirp->tcp_now++; /* for timestamps */
+}
+
+/*
+ * Cancel all timers for TCP tp.
+ */
+void
+tcp_canceltimers(struct tcpcb *tp)
+{
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = 0;
+}
+
+const int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
+ { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+/*
+ * TCP timer processing.
+ */
+static struct tcpcb *
+tcp_timers(register struct tcpcb *tp, int timer)
+{
+ register int rexmt;
+
+ DEBUG_CALL("tcp_timers");
+
+ switch (timer) {
+
+ /*
+ * 2 MSL timeout in shutdown went off. If we're closed but
+ * still waiting for peer to close and connection has been idle
+ * too long, or if 2MSL time is up from TIME_WAIT, delete connection
+ * control block. Otherwise, check again in a bit.
+ */
+ case TCPT_2MSL:
+ if (tp->t_state != TCPS_TIME_WAIT &&
+ tp->t_idle <= TCP_MAXIDLE)
+ tp->t_timer[TCPT_2MSL] = TCPTV_KEEPINTVL;
+ else
+ tp = tcp_close(tp);
+ break;
+
+ /*
+ * Retransmission timer went off. Message has not
+ * been acked within retransmit interval. Back off
+ * to a longer retransmit interval and retransmit one segment.
+ */
+ case TCPT_REXMT:
+
+ /*
+ * XXXXX If a packet has timed out, then remove all the queued
+ * packets for that session.
+ */
+
+ if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
+ /*
+ * This is a hack to suit our terminal server here at the uni of canberra
+ * since they have trouble with zeroes... It usually lets them through
+ * unharmed, but under some conditions, it'll eat the zeros. If we
+ * keep retransmitting it, it'll keep eating the zeroes, so we keep
+ * retransmitting, and eventually the connection dies...
+ * (this only happens on incoming data)
+ *
+ * So, if we were gonna drop the connection from too many retransmits,
+ * don't... instead halve the t_maxseg, which might break up the NULLs and
+ * let them through
+ *
+ * *sigh*
+ */
+
+ tp->t_maxseg >>= 1;
+ if (tp->t_maxseg < 32) {
+ /*
+ * We tried our best, now the connection must die!
+ */
+ tp->t_rxtshift = TCP_MAXRXTSHIFT;
+ tp = tcp_drop(tp, tp->t_softerror);
+ /* tp->t_softerror : ETIMEDOUT); */ /* XXX */
+ return (tp); /* XXX */
+ }
+
+ /*
+ * Set rxtshift to 6, which is still at the maximum
+ * backoff time
+ */
+ tp->t_rxtshift = 6;
+ }
+ rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+ TCPT_RANGESET(tp->t_rxtcur, rexmt,
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * If losing, let the lower level know and try for
+ * a better route. Also, if we backed off this far,
+ * our srtt estimate is probably bogus. Clobber it
+ * so we'll take the next rtt measurement as our srtt;
+ * move the current srtt into rttvar to keep the current
+ * retransmit times until then.
+ */
+ if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
+ tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
+ tp->t_srtt = 0;
+ }
+ tp->snd_nxt = tp->snd_una;
+ /*
+ * If timing a segment in this window, stop the timer.
+ */
+ tp->t_rtt = 0;
+ /*
+ * Close the congestion window down to one segment
+ * (we'll open it by one segment for each ack we get).
+ * Since we probably have a window's worth of unacked
+ * data accumulated, this "slow start" keeps us from
+ * dumping all that data as back-to-back packets (which
+ * might overwhelm an intermediate gateway).
+ *
+ * There are two phases to the opening: Initially we
+ * open by one mss on each ack. This makes the window
+ * size increase exponentially with time. If the
+ * window is larger than the path can handle, this
+ * exponential growth results in dropped packet(s)
+ * almost immediately. To get more time between
+ * drops but still "push" the network to take advantage
+ * of improving conditions, we switch from exponential
+ * to linear window opening at some threshold size.
+ * For a threshold, we use half the current window
+ * size, truncated to a multiple of the mss.
+ *
+ * (the minimum cwnd that will give us exponential
+ * growth is 2 mss. We don't allow the threshold
+ * to go below this.)
+ */
+ {
+ u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+ if (win < 2)
+ win = 2;
+ tp->snd_cwnd = tp->t_maxseg;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_dupacks = 0;
+ }
+ (void) tcp_output(tp);
+ break;
+
+ /*
+ * Persistence timer into zero window.
+ * Force a byte to be output, if possible.
+ */
+ case TCPT_PERSIST:
+ tcp_setpersist(tp);
+ tp->t_force = 1;
+ (void) tcp_output(tp);
+ tp->t_force = 0;
+ break;
+
+ /*
+ * Keep-alive timer went off; send something
+ * or drop connection if idle for too long.
+ */
+ case TCPT_KEEP:
+ if (tp->t_state < TCPS_ESTABLISHED)
+ goto dropit;
+
+ if ((SO_OPTIONS) && tp->t_state <= TCPS_CLOSE_WAIT) {
+ if (tp->t_idle >= TCPTV_KEEP_IDLE + TCP_MAXIDLE)
+ goto dropit;
+ /*
+ * Send a packet designed to force a response
+ * if the peer is up and reachable:
+ * either an ACK if the connection is still alive,
+ * or an RST if the peer has closed the connection
+ * due to timeout or reboot.
+ * Using sequence number tp->snd_una-1
+ * causes the transmitted zero-length segment
+ * to lie outside the receive window;
+ * by the protocol spec, this requires the
+ * correspondent TCP to respond.
+ */
+ tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt, tp->snd_una - 1, 0);
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
+ } else
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
+ break;
+
+ dropit:
+ tp = tcp_drop(tp, 0);
+ break;
+ }
+
+ return (tp);
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_timer.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_timer.h
new file mode 100644
index 0000000..ff17914
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_timer.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93
+ * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp
+ */
+
+#ifndef _TCP_TIMER_H_
+#define _TCP_TIMER_H_
+
+/*
+ * Definitions of the TCP timers. These timers are counted
+ * down PR_SLOWHZ times a second.
+ */
+#define TCPT_NTIMERS 4
+
+#define TCPT_REXMT 0 /* retransmit */
+#define TCPT_PERSIST 1 /* retransmit persistence */
+#define TCPT_KEEP 2 /* keep alive */
+#define TCPT_2MSL 3 /* 2*msl quiet time timer */
+
+/*
+ * The TCPT_REXMT timer is used to force retransmissions.
+ * The TCP has the TCPT_REXMT timer set whenever segments
+ * have been sent for which ACKs are expected but not yet
+ * received. If an ACK is received which advances tp->snd_una,
+ * then the retransmit timer is cleared (if there are no more
+ * outstanding segments) or reset to the base value (if there
+ * are more ACKs expected). Whenever the retransmit timer goes off,
+ * we retransmit one unacknowledged segment, and do a backoff
+ * on the retransmit timer.
+ *
+ * The TCPT_PERSIST timer is used to keep window size information
+ * flowing even if the window goes shut. If all previous transmissions
+ * have been acknowledged (so that there are no retransmissions in progress),
+ * and the window is too small to bother sending anything, then we start
+ * the TCPT_PERSIST timer. When it expires, if the window is nonzero,
+ * we go to transmit state. Otherwise, at intervals send a single byte
+ * into the peer's window to force him to update our window information.
+ * We do this at most as often as TCPT_PERSMIN time intervals,
+ * but no more frequently than the current estimate of round-trip
+ * packet time. The TCPT_PERSIST timer is cleared whenever we receive
+ * a window update from the peer.
+ *
+ * The TCPT_KEEP timer is used to keep connections alive. If an
+ * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time,
+ * but not yet established, then we drop the connection. Once the connection
+ * is established, if the connection is idle for TCPTV_KEEP_IDLE time
+ * (and keepalives have been enabled on the socket), we begin to probe
+ * the connection. We force the peer to send us a segment by sending:
+ * <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
+ * This segment is (deliberately) outside the window, and should elicit
+ * an ack segment in response from the peer. If, despite the TCPT_KEEP
+ * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE
+ * amount of time probing, then we drop the connection.
+ */
+
+/*
+ * Time constants.
+ */
+#define TCPTV_MSL ( 5*PR_SLOWHZ) /* max seg lifetime (hah!) */
+
+#define TCPTV_SRTTBASE 0 /* base roundtrip time;
+ if 0, no idea yet */
+#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
+
+#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */
+#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
+
+#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
+#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
+#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
+#define TCPTV_KEEPCNT 8 /* max probes before drop */
+
+#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
+#define TCPTV_REXMTMAX ( 12*PR_SLOWHZ) /* max allowable REXMT value */
+
+#define TCP_LINGERTIME 120 /* linger at most 2 minutes */
+
+#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
+
+
+/*
+ * Force a time value to be in a certain range.
+ */
+#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
+ (tv) = (value); \
+ if ((tv) < (tvmin)) \
+ (tv) = (tvmin); \
+ else if ((tv) > (tvmax)) \
+ (tv) = (tvmax); \
+}
+
+extern const int tcp_backoff[];
+
+struct tcpcb;
+
+void tcp_fasttimo(Slirp *);
+void tcp_slowtimo(Slirp *);
+void tcp_canceltimers(struct tcpcb *);
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_var.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_var.h
new file mode 100644
index 0000000..004193f
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcp_var.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_var.h 8.3 (Berkeley) 4/10/94
+ * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp
+ */
+
+#ifndef _TCP_VAR_H_
+#define _TCP_VAR_H_
+
+#include "tcpip.h"
+#include "tcp_timer.h"
+
+/*
+ * Tcp control block, one per tcp; fields:
+ */
+struct tcpcb {
+ struct tcpiphdr *seg_next; /* sequencing queue */
+ struct tcpiphdr *seg_prev;
+ short t_state; /* state of this connection */
+ short t_timer[TCPT_NTIMERS]; /* tcp timers */
+ short t_rxtshift; /* log(2) of rexmt exp. backoff */
+ short t_rxtcur; /* current retransmit value */
+ short t_dupacks; /* consecutive dup acks recd */
+ u_short t_maxseg; /* maximum segment size */
+ char t_force; /* 1 if forcing out a byte */
+ u_short t_flags;
+#define TF_ACKNOW 0x0001 /* ack peer immediately */
+#define TF_DELACK 0x0002 /* ack, but try to delay it */
+#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */
+#define TF_NOOPT 0x0008 /* don't use tcp options */
+#define TF_SENTFIN 0x0010 /* have sent FIN */
+#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */
+#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */
+#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */
+#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */
+#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */
+
+ struct tcpiphdr t_template; /* static skeletal packet for xmit */
+
+ struct socket *t_socket; /* back pointer to socket */
+/*
+ * The following fields are used as in the protocol specification.
+ * See RFC783, Dec. 1981, page 21.
+ */
+/* send sequence variables */
+ tcp_seq snd_una; /* send unacknowledged */
+ tcp_seq snd_nxt; /* send next */
+ tcp_seq snd_up; /* send urgent pointer */
+ tcp_seq snd_wl1; /* window update seg seq number */
+ tcp_seq snd_wl2; /* window update seg ack number */
+ tcp_seq iss; /* initial send sequence number */
+ uint32_t snd_wnd; /* send window */
+/* receive sequence variables */
+ uint32_t rcv_wnd; /* receive window */
+ tcp_seq rcv_nxt; /* receive next */
+ tcp_seq rcv_up; /* receive urgent pointer */
+ tcp_seq irs; /* initial receive sequence number */
+/*
+ * Additional variables for this implementation.
+ */
+/* receive variables */
+ tcp_seq rcv_adv; /* advertised window */
+/* retransmit variables */
+ tcp_seq snd_max; /* highest sequence number sent;
+ * used to recognize retransmits
+ */
+/* congestion control (for slow start, source quench, retransmit after loss) */
+ uint32_t snd_cwnd; /* congestion-controlled window */
+ uint32_t snd_ssthresh; /* snd_cwnd size threshold for
+ * for slow start exponential to
+ * linear switch
+ */
+/*
+ * transmit timing stuff. See below for scale of srtt and rttvar.
+ * "Variance" is actually smoothed difference.
+ */
+ short t_idle; /* inactivity time */
+ short t_rtt; /* round trip time */
+ tcp_seq t_rtseq; /* sequence number being timed */
+ short t_srtt; /* smoothed round-trip time */
+ short t_rttvar; /* variance in round-trip time */
+ u_short t_rttmin; /* minimum rtt allowed */
+ uint32_t max_sndwnd; /* largest window peer has offered */
+
+/* out-of-band data */
+ char t_oobflags; /* have some */
+ char t_iobc; /* input character */
+#define TCPOOB_HAVEDATA 0x01
+#define TCPOOB_HADDATA 0x02
+ short t_softerror; /* possible error not yet reported */
+
+/* RFC 1323 variables */
+ u_char snd_scale; /* window scaling for send window */
+ u_char rcv_scale; /* window scaling for recv window */
+ u_char request_r_scale; /* pending window scaling */
+ u_char requested_s_scale;
+ uint32_t ts_recent; /* timestamp echo data */
+ uint32_t ts_recent_age; /* when last updated */
+ tcp_seq last_ack_sent;
+
+};
+
+#define sototcpcb(so) ((so)->so_tcpcb)
+
+/*
+ * The smoothed round-trip time and estimated variance
+ * are stored as fixed point numbers scaled by the values below.
+ * For convenience, these scales are also used in smoothing the average
+ * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
+ * With these scales, srtt has 3 bits to the right of the binary point,
+ * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the
+ * binary point, and is smoothed with an ALPHA of 0.75.
+ */
+#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */
+#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */
+#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */
+#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */
+
+/*
+ * The initial retransmission should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ * This macro assumes that the value of TCP_RTTVAR_SCALE
+ * is the same as the multiplier for rttvar.
+ */
+#define TCP_REXMTVAL(tp) \
+ (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar)
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcpip.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcpip.h
new file mode 100644
index 0000000..7974ce3
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tcpip.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcpip.h 8.1 (Berkeley) 6/10/93
+ * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp
+ */
+
+#ifndef _TCPIP_H_
+#define _TCPIP_H_
+
+/*
+ * Tcp+ip header, after ip options removed.
+ */
+struct tcpiphdr {
+ struct ipovly ti_i; /* overlaid ip structure */
+ struct tcphdr ti_t; /* tcp header */
+};
+#define ti_mbuf ti_i.ih_mbuf.mptr
+#define ti_x1 ti_i.ih_x1
+#define ti_pr ti_i.ih_pr
+#define ti_len ti_i.ih_len
+#define ti_src ti_i.ih_src
+#define ti_dst ti_i.ih_dst
+#define ti_sport ti_t.th_sport
+#define ti_dport ti_t.th_dport
+#define ti_seq ti_t.th_seq
+#define ti_ack ti_t.th_ack
+#define ti_x2 ti_t.th_x2
+#define ti_off ti_t.th_off
+#define ti_flags ti_t.th_flags
+#define ti_win ti_t.th_win
+#define ti_sum ti_t.th_sum
+#define ti_urp ti_t.th_urp
+
+#define tcpiphdr2qlink(T) ((struct qlink*)(((char*)(T)) - sizeof(struct qlink)))
+#define qlink2tcpiphdr(Q) ((struct tcpiphdr*)(((char*)(Q)) + sizeof(struct qlink)))
+#define tcpiphdr_next(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->next)
+#define tcpiphdr_prev(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->prev)
+#define tcpfrag_list_first(T) qlink2tcpiphdr((T)->seg_next)
+#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T))
+#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T))
+
+/*
+ * Just a clean way to get to the first byte
+ * of the packet
+ */
+struct tcpiphdr_2 {
+ struct tcpiphdr dummy;
+ char first_char;
+};
+
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tftp.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tftp.h
new file mode 100644
index 0000000..b9f0847
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/tftp.h
@@ -0,0 +1,43 @@
+/* tftp defines */
+
+#define TFTP_SESSIONS_MAX 3
+
+#define TFTP_SERVER 69
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+#define TFTP_OACK 6
+
+#define TFTP_FILENAME_MAX 512
+
+struct tftp_t {
+ struct ip ip;
+ struct udphdr udp;
+ uint16_t tp_op;
+ union {
+ struct {
+ uint16_t tp_block_nr;
+ uint8_t tp_buf[512];
+ } tp_data;
+ struct {
+ uint16_t tp_error_code;
+ uint8_t tp_msg[512];
+ } tp_error;
+ uint8_t tp_buf[512 + 2];
+ } x;
+};
+
+struct tftp_session {
+ Slirp *slirp;
+ char *filename;
+
+ struct in_addr client_ip;
+ uint16_t client_port;
+
+ int timestamp;
+};
+
+void tftp_input(struct mbuf *m);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/udp.c b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/udp.c
new file mode 100644
index 0000000..2d04f12
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/udp.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94
+ * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+static uint8_t udp_tos(struct socket *so);
+
+void
+udp_init(Slirp *slirp)
+{
+ slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb;
+ slirp->udp_last_so = &slirp->udb;
+}
+/* m->m_data points at ip packet header
+ * m->m_len length ip packet
+ * ip->ip_len length data (IPDU)
+ */
+void
+udp_input(register struct mbuf *m, int iphlen)
+{
+ Slirp *slirp = m->slirp;
+ register struct ip *ip;
+ register struct udphdr *uh;
+ int len;
+ struct ip save_ip;
+ struct socket *so;
+
+ DEBUG_CALL("udp_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("iphlen = %d", iphlen);
+
+ /*
+ * Strip IP options, if any; should skip this,
+ * make available to user, and use on returned packets,
+ * but we don't yet have a way to check the checksum
+ * with options still present.
+ */
+ if(iphlen > sizeof(struct ip)) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen = sizeof(struct ip);
+ }
+
+ /*
+ * Get IP and UDP header together in first mbuf.
+ */
+ ip = mtod(m, struct ip *);
+ uh = (struct udphdr *)((caddr_t)ip + iphlen);
+
+ /*
+ * Make mbuf data length reflect UDP length.
+ * If not enough data to reflect UDP length, drop.
+ */
+ len = ntohs((uint16_t)uh->uh_ulen);
+
+ if (ip->ip_len != len) {
+ if (len > ip->ip_len) {
+ goto bad;
+ }
+ m_adj(m, len - ip->ip_len);
+ ip->ip_len = len;
+ }
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip = *ip;
+ save_ip.ip_len+= iphlen; /* tcp_input subtracts this */
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (uh->uh_sum) {
+ memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr));
+ ((struct ipovly *)ip)->ih_x1 = 0;
+ ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+ if(cksum(m, len + sizeof(struct ip))) {
+ goto bad;
+ }
+ }
+
+ /*
+ * handle DHCP/BOOTP
+ */
+ if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
+ bootp_input(m);
+ goto bad;
+ }
+
+ if (slirp->restricted) {
+ goto bad;
+ }
+
+#if 0
+ /*
+ * handle TFTP
+ */
+ if (ntohs(uh->uh_dport) == TFTP_SERVER) {
+ tftp_input(m);
+ goto bad;
+ }
+#endif
+
+ /*
+ * Locate pcb for datagram.
+ */
+ so = slirp->udp_last_so;
+ if (so->so_lport != uh->uh_sport ||
+ so->so_laddr.s_addr != ip->ip_src.s_addr) {
+ struct socket *tmp;
+
+ for (tmp = slirp->udb.so_next; tmp != &slirp->udb;
+ tmp = tmp->so_next) {
+ if (tmp->so_lport == uh->uh_sport &&
+ tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
+ so = tmp;
+ break;
+ }
+ }
+ if (tmp == &slirp->udb) {
+ so = NULL;
+ } else {
+ slirp->udp_last_so = so;
+ }
+ }
+
+ if (so == NULL) {
+ /*
+ * If there's no socket for this packet,
+ * create one
+ */
+ so = socreate(slirp);
+ if (!so) {
+ goto bad;
+ }
+ if(udp_attach(so) == -1) {
+ DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
+ errno,strerror(errno)));
+ sofree(so);
+ goto bad;
+ }
+
+ /*
+ * Setup fields
+ */
+ so->so_laddr = ip->ip_src;
+ so->so_lport = uh->uh_sport;
+
+ if ((so->so_iptos = udp_tos(so)) == 0)
+ so->so_iptos = ip->ip_tos;
+
+ /*
+ * XXXXX Here, check if it's in udpexec_list,
+ * and if it is, do the fork_exec() etc.
+ */
+ }
+
+ so->so_faddr = ip->ip_dst; /* XXX */
+ so->so_fport = uh->uh_dport; /* XXX */
+
+ iphlen += sizeof(struct udphdr);
+ m->m_len -= iphlen;
+ m->m_data += iphlen;
+
+ /*
+ * Now we sendto() the packet.
+ */
+ if(sosendto(so,m) == -1) {
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip=save_ip;
+ DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
+ icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ }
+
+ m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
+
+ /* restore the orig mbuf packet */
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip=save_ip;
+ so->so_m=m; /* ICMP backup */
+
+ return;
+bad:
+ m_freem(m);
+ return;
+}
+
+int udp_output2(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos)
+{
+ register struct udpiphdr *ui;
+ int error = 0;
+
+ DEBUG_CALL("udp_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr);
+ DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr);
+
+ /*
+ * Adjust for header
+ */
+ m->m_data -= sizeof(struct udpiphdr);
+ m->m_len += sizeof(struct udpiphdr);
+
+ /*
+ * Fill in mbuf with extended UDP header
+ * and addresses and length put into network format.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ memset(&ui->ui_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+ ui->ui_x1 = 0;
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = htons(m->m_len - sizeof(struct ip));
+ /* XXXXX Check for from-one-location sockets, or from-any-location sockets */
+ ui->ui_src = saddr->sin_addr;
+ ui->ui_dst = daddr->sin_addr;
+ ui->ui_sport = saddr->sin_port;
+ ui->ui_dport = daddr->sin_port;
+ ui->ui_ulen = ui->ui_len;
+
+ /*
+ * Stuff checksum and output datagram.
+ */
+ ui->ui_sum = 0;
+ if ((ui->ui_sum = cksum(m, m->m_len)) == 0)
+ ui->ui_sum = 0xffff;
+ ((struct ip *)ui)->ip_len = m->m_len;
+
+ ((struct ip *)ui)->ip_ttl = IPDEFTTL;
+ ((struct ip *)ui)->ip_tos = iptos;
+
+ error = ip_output(so, m);
+
+ return (error);
+}
+
+int udp_output(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *addr)
+
+{
+ Slirp *slirp = so->slirp;
+ struct sockaddr_in saddr, daddr;
+
+ saddr = *addr;
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
+
+ if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
+ saddr.sin_addr = slirp->vhost_addr;
+ } else if (addr->sin_addr.s_addr == loopback_addr.s_addr ||
+ so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
+ saddr.sin_addr = so->so_faddr;
+ }
+ }
+ daddr.sin_addr = so->so_laddr;
+ daddr.sin_port = so->so_lport;
+
+ return udp_output2(so, m, &saddr, &daddr, so->so_iptos);
+}
+
+int
+udp_attach(struct socket *so)
+{
+ if((so->s = os_socket(AF_INET,SOCK_DGRAM,0)) != -1) {
+ so->so_expire = curtime + SO_EXPIRE;
+ insque(so, &so->slirp->udb);
+ }
+ return(so->s);
+}
+
+void
+udp_detach(struct socket *so)
+{
+ closesocket(so->s);
+ sofree(so);
+}
+
+static const struct tos_t udptos[] = {
+ {0, 53, IPTOS_LOWDELAY, 0}, /* DNS */
+ {0, 0, 0, 0}
+};
+
+static uint8_t
+udp_tos(struct socket *so)
+{
+ int i = 0;
+
+ while(udptos[i].tos) {
+ if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) ||
+ (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) {
+ so->so_emu = udptos[i].emu;
+ return udptos[i].tos;
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+struct socket *
+udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
+ u_int lport, int flags)
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ socklen_t addrlen = sizeof(struct sockaddr_in), opt = 1;
+
+ so = socreate(slirp);
+ if (!so) {
+ return NULL;
+ }
+ so->s = os_socket(AF_INET,SOCK_DGRAM,0);
+ so->so_expire = curtime + SO_EXPIRE;
+ insque(so, &slirp->udb);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = haddr;
+ addr.sin_port = hport;
+
+ if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) {
+ udp_detach(so);
+ return NULL;
+ }
+ setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+
+ getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ if (addr.sin_addr.s_addr == 0 ||
+ addr.sin_addr.s_addr == loopback_addr.s_addr) {
+ so->so_faddr = slirp->vhost_addr;
+ } else {
+ so->so_faddr = addr.sin_addr;
+ }
+ so->so_lport = lport;
+ so->so_laddr.s_addr = laddr;
+ if (flags != SS_FACCEPTONCE)
+ so->so_expire = 0;
+
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_ISFCONNECTED | flags;
+
+ return so;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/udp.h b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/udp.h
new file mode 100644
index 0000000..9b5c3cf
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/slirp/udp.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp.h 8.1 (Berkeley) 6/10/93
+ * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp
+ */
+
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#define UDP_TTL 0x60
+#define UDP_UDPDATALEN 16192
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr {
+ uint16_t uh_sport; /* source port */
+ uint16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ uint16_t uh_sum; /* udp checksum */
+};
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr {
+ struct ipovly ui_i; /* overlaid ip structure */
+ struct udphdr ui_u; /* udp header */
+};
+#define ui_mbuf ui_i.ih_mbuf.mptr
+#define ui_x1 ui_i.ih_x1
+#define ui_pr ui_i.ih_pr
+#define ui_len ui_i.ih_len
+#define ui_src ui_i.ih_src
+#define ui_dst ui_i.ih_dst
+#define ui_sport ui_u.uh_sport
+#define ui_dport ui_u.uh_dport
+#define ui_ulen ui_u.uh_ulen
+#define ui_sum ui_u.uh_sum
+
+/*
+ * Names for UDP sysctl objects
+ */
+#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */
+#define UDPCTL_MAXID 2
+
+struct mbuf;
+
+void udp_init(Slirp *);
+void udp_input(register struct mbuf *, int);
+int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *);
+int udp_attach(struct socket *);
+void udp_detach(struct socket *);
+struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
+ int);
+int udp_output2(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos);
+#endif
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/softfp.c b/jslinux-2019-12-21/tinyemu-2019-12-21/softfp.c
new file mode 100644
index 0000000..2a1aad0
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/softfp.c
@@ -0,0 +1,87 @@
+/*
+ * SoftFP Library
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "cutils.h"
+#include "softfp.h"
+
+static inline int clz32(uint32_t a)
+{
+ int r;
+ if (a == 0) {
+ r = 32;
+ } else {
+ r = __builtin_clz(a);
+ }
+ return r;
+}
+
+static inline int clz64(uint64_t a)
+{
+ int r;
+ if (a == 0) {
+ r = 64;
+ } else
+ {
+ r = __builtin_clzll(a);
+ }
+ return r;
+}
+
+#ifdef HAVE_INT128
+static inline int clz128(uint128_t a)
+{
+ int r;
+ if (a == 0) {
+ r = 128;
+ } else
+ {
+ uint64_t ah, al;
+ ah = a >> 64;
+ al = a;
+ if (ah != 0)
+ r = __builtin_clzll(ah);
+ else
+ r = __builtin_clzll(al) + 64;
+ }
+ return r;
+}
+#endif
+
+#define F_SIZE 32
+#include "softfp_template.h"
+
+#define F_SIZE 64
+#include "softfp_template.h"
+
+#ifdef HAVE_INT128
+
+#define F_SIZE 128
+#include "softfp_template.h"
+
+#endif
+
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/softfp.h b/jslinux-2019-12-21/tinyemu-2019-12-21/softfp.h
new file mode 100644
index 0000000..974c7ec
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/softfp.h
@@ -0,0 +1,181 @@
+/*
+ * SoftFP Library
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef SOFTFP_H
+#define SOFTFP_H
+
+#include <inttypes.h>
+#include "cutils.h"
+
+typedef enum {
+ RM_RNE, /* Round to Nearest, ties to Even */
+ RM_RTZ, /* Round towards Zero */
+ RM_RDN, /* Round Down */
+ RM_RUP, /* Round Up */
+ RM_RMM, /* Round to Nearest, ties to Max Magnitude */
+} RoundingModeEnum;
+
+#define FFLAG_INVALID_OP (1 << 4)
+#define FFLAG_DIVIDE_ZERO (1 << 3)
+#define FFLAG_OVERFLOW (1 << 2)
+#define FFLAG_UNDERFLOW (1 << 1)
+#define FFLAG_INEXACT (1 << 0)
+
+#define FCLASS_NINF (1 << 0)
+#define FCLASS_NNORMAL (1 << 1)
+#define FCLASS_NSUBNORMAL (1 << 2)
+#define FCLASS_NZERO (1 << 3)
+#define FCLASS_PZERO (1 << 4)
+#define FCLASS_PSUBNORMAL (1 << 5)
+#define FCLASS_PNORMAL (1 << 6)
+#define FCLASS_PINF (1 << 7)
+#define FCLASS_SNAN (1 << 8)
+#define FCLASS_QNAN (1 << 9)
+
+typedef enum {
+ FMINMAX_PROP, /* min(1, qNaN/sNaN) -> qNaN */
+ FMINMAX_IEEE754_2008, /* min(1, qNaN) -> 1, min(1, sNaN) -> qNaN */
+ FMINMAX_IEEE754_201X, /* min(1, qNaN/sNaN) -> 1 */
+} SoftFPMinMaxTypeEnum;
+
+typedef uint32_t sfloat32;
+typedef uint64_t sfloat64;
+#ifdef HAVE_INT128
+typedef uint128_t sfloat128;
+#endif
+
+/* 32 bit floats */
+
+#define FSIGN_MASK32 (1 << 31)
+
+sfloat32 add_sf32(sfloat32 a, sfloat32 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 sub_sf32(sfloat32 a, sfloat32 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 mul_sf32(sfloat32 a, sfloat32 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 div_sf32(sfloat32 a, sfloat32 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 sqrt_sf32(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 fma_sf32(sfloat32 a, sfloat32 b, sfloat32 c, RoundingModeEnum rm, uint32_t *pfflags);
+
+sfloat32 min_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
+sfloat32 max_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
+int eq_quiet_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags);
+int le_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags);
+int lt_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags);
+uint32_t fclass_sf32(sfloat32 a);
+
+sfloat64 cvt_sf32_sf64(sfloat32 a, uint32_t *pfflags);
+sfloat32 cvt_sf64_sf32(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+int32_t cvt_sf32_i32(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint32_t cvt_sf32_u32(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
+int64_t cvt_sf32_i64(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint64_t cvt_sf32_u64(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
+#ifdef HAVE_INT128
+int128_t cvt_sf32_i128(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint128_t cvt_sf32_u128(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
+#endif
+sfloat32 cvt_i32_sf32(int32_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 cvt_u32_sf32(uint32_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 cvt_i64_sf32(int64_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 cvt_u64_sf32(uint64_t a, RoundingModeEnum rm, uint32_t *pfflags);
+#ifdef HAVE_INT128
+sfloat32 cvt_i128_sf32(int128_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat32 cvt_u128_sf32(uint128_t a, RoundingModeEnum rm, uint32_t *pfflags);
+#endif
+
+/* 64 bit floats */
+
+#define FSIGN_MASK64 ((uint64_t)1 << 63)
+
+sfloat64 add_sf64(sfloat64 a, sfloat64 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 sub_sf64(sfloat64 a, sfloat64 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 mul_sf64(sfloat64 a, sfloat64 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 div_sf64(sfloat64 a, sfloat64 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 sqrt_sf64(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 fma_sf64(sfloat64 a, sfloat64 b, sfloat64 c, RoundingModeEnum rm, uint32_t *pfflags);
+
+sfloat64 min_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
+sfloat64 max_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
+int eq_quiet_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags);
+int le_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags);
+int lt_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags);
+uint32_t fclass_sf64(sfloat64 a);
+
+sfloat64 cvt_sf32_sf64(sfloat32 a, uint32_t *pfflags);
+sfloat32 cvt_sf64_sf32(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+int32_t cvt_sf64_i32(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint32_t cvt_sf64_u32(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+int64_t cvt_sf64_i64(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint64_t cvt_sf64_u64(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+#ifdef HAVE_INT128
+int128_t cvt_sf64_i128(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint128_t cvt_sf64_u128(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
+#endif
+sfloat64 cvt_i32_sf64(int32_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 cvt_u32_sf64(uint32_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 cvt_i64_sf64(int64_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 cvt_u64_sf64(uint64_t a, RoundingModeEnum rm, uint32_t *pfflags);
+#ifdef HAVE_INT128
+sfloat64 cvt_i128_sf64(int128_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat64 cvt_u128_sf64(uint128_t a, RoundingModeEnum rm, uint32_t *pfflags);
+#endif
+
+/* 128 bit floats */
+
+#ifdef HAVE_INT128
+
+#define FSIGN_MASK128 ((uint128_t)1 << 127)
+
+sfloat128 add_sf128(sfloat128 a, sfloat128 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 sub_sf128(sfloat128 a, sfloat128 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 mul_sf128(sfloat128 a, sfloat128 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 div_sf128(sfloat128 a, sfloat128 b, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 sqrt_sf128(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 fma_sf128(sfloat128 a, sfloat128 b, sfloat128 c, RoundingModeEnum rm, uint32_t *pfflags);
+
+sfloat128 min_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
+sfloat128 max_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
+int eq_quiet_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags);
+int le_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags);
+int lt_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags);
+uint32_t fclass_sf128(sfloat128 a);
+
+sfloat128 cvt_sf32_sf128(sfloat32 a, uint32_t *pfflags);
+sfloat32 cvt_sf128_sf32(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 cvt_sf64_sf128(sfloat64 a, uint32_t *pfflags);
+sfloat64 cvt_sf128_sf64(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+
+int32_t cvt_sf128_i32(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint32_t cvt_sf128_u32(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+int64_t cvt_sf128_i64(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint64_t cvt_sf128_u64(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+int128_t cvt_sf128_i128(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+uint128_t cvt_sf128_u128(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 cvt_i32_sf128(int32_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 cvt_u32_sf128(uint32_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 cvt_i64_sf128(int64_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 cvt_u64_sf128(uint64_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 cvt_i128_sf128(int128_t a, RoundingModeEnum rm, uint32_t *pfflags);
+sfloat128 cvt_u128_sf128(uint128_t a, RoundingModeEnum rm, uint32_t *pfflags);
+
+#endif
+
+#endif /* SOFTFP_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/softfp_template.h b/jslinux-2019-12-21/tinyemu-2019-12-21/softfp_template.h
new file mode 100644
index 0000000..88dbf66
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/softfp_template.h
@@ -0,0 +1,1129 @@
+/*
+ * SoftFP Library
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#if F_SIZE == 32
+#define F_UINT uint32_t
+#define F_ULONG uint64_t
+#define MANT_SIZE 23
+#define EXP_SIZE 8
+#elif F_SIZE == 64
+#define F_UHALF uint32_t
+#define F_UINT uint64_t
+#ifdef HAVE_INT128
+#define F_ULONG uint128_t
+#endif
+#define MANT_SIZE 52
+#define EXP_SIZE 11
+#elif F_SIZE == 128
+#define F_UHALF uint64_t
+#define F_UINT uint128_t
+#define MANT_SIZE 112
+#define EXP_SIZE 15
+#else
+#error unsupported F_SIZE
+#endif
+
+#define EXP_MASK ((1 << EXP_SIZE) - 1)
+#define MANT_MASK (((F_UINT)1 << MANT_SIZE) - 1)
+#define SIGN_MASK ((F_UINT)1 << (F_SIZE - 1))
+#define IMANT_SIZE (F_SIZE - 2) /* internal mantissa size */
+#define RND_SIZE (IMANT_SIZE - MANT_SIZE)
+#define QNAN_MASK ((F_UINT)1 << (MANT_SIZE - 1))
+
+/* quiet NaN */
+#define F_QNAN glue(F_QNAN, F_SIZE)
+#define clz glue(clz, F_SIZE)
+#define pack_sf glue(pack_sf, F_SIZE)
+#define unpack_sf glue(unpack_sf, F_SIZE)
+#define rshift_rnd glue(rshift_rnd, F_SIZE)
+#define round_pack_sf glue(roundpack_sf, F_SIZE)
+#define normalize_sf glue(normalize_sf, F_SIZE)
+#define normalize2_sf glue(normalize2_sf, F_SIZE)
+#define issignan_sf glue(issignan_sf, F_SIZE)
+#define isnan_sf glue(isnan_sf, F_SIZE)
+#define add_sf glue(add_sf, F_SIZE)
+#define mul_sf glue(mul_sf, F_SIZE)
+#define fma_sf glue(fma_sf, F_SIZE)
+#define div_sf glue(div_sf, F_SIZE)
+#define sqrt_sf glue(sqrt_sf, F_SIZE)
+#define normalize_subnormal_sf glue(normalize_subnormal_sf, F_SIZE)
+#define divrem_u glue(divrem_u, F_SIZE)
+#define sqrtrem_u glue(sqrtrem_u, F_SIZE)
+#define mul_u glue(mul_u, F_SIZE)
+#define cvt_sf32_sf glue(cvt_sf32_sf, F_SIZE)
+#define cvt_sf64_sf glue(cvt_sf64_sf, F_SIZE)
+
+static const F_UINT F_QNAN = (((F_UINT)EXP_MASK << MANT_SIZE) | ((F_UINT)1 << (MANT_SIZE - 1)));
+
+static inline F_UINT pack_sf(uint32_t a_sign, uint32_t a_exp, F_UINT a_mant)
+{
+ return ((F_UINT)a_sign << (F_SIZE - 1)) |
+ ((F_UINT)a_exp << MANT_SIZE) |
+ (a_mant & MANT_MASK);
+}
+
+static inline F_UINT unpack_sf(uint32_t *pa_sign, int32_t *pa_exp,
+ F_UINT a)
+{
+ *pa_sign = a >> (F_SIZE - 1);
+ *pa_exp = (a >> MANT_SIZE) & EXP_MASK;
+ return a & MANT_MASK;
+}
+
+static F_UINT rshift_rnd(F_UINT a, int d)
+{
+ F_UINT mask;
+ if (d != 0) {
+ if (d >= F_SIZE) {
+ a = (a != 0);
+ } else {
+ mask = ((F_UINT)1 << d) - 1;
+ a = (a >> d) | ((a & mask) != 0);
+ }
+ }
+ return a;
+}
+
+/* a_mant is considered to have its MSB at F_SIZE - 2 bits */
+static F_UINT round_pack_sf(uint32_t a_sign, int a_exp, F_UINT a_mant,
+ RoundingModeEnum rm, uint32_t *pfflags)
+{
+ int diff;
+ uint32_t addend, rnd_bits;
+
+ switch(rm) {
+ case RM_RNE:
+ case RM_RMM:
+ addend = (1 << (RND_SIZE - 1));
+ break;
+ case RM_RTZ:
+ addend = 0;
+ break;
+ default:
+ case RM_RDN:
+ case RM_RUP:
+ // printf("s=%d rm=%d m=%x\n", a_sign, rm, a_mant);
+ if (a_sign ^ (rm & 1))
+ addend = (1 << RND_SIZE) - 1;
+ else
+ addend = 0;
+ break;
+ }
+
+ /* potentially subnormal */
+ if (a_exp <= 0) {
+ BOOL is_subnormal;
+ /* Note: we set the underflow flag if the rounded result
+ is subnormal and inexact */
+ is_subnormal = (a_exp < 0 ||
+ (a_mant + addend) < ((F_UINT)1 << (F_SIZE - 1)));
+ diff = 1 - a_exp;
+ a_mant = rshift_rnd(a_mant, diff);
+ rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);
+ if (is_subnormal && rnd_bits != 0) {
+ *pfflags |= FFLAG_UNDERFLOW;
+ }
+ a_exp = 1;
+ } else {
+ rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);
+ }
+ if (rnd_bits != 0)
+ *pfflags |= FFLAG_INEXACT;
+ a_mant = (a_mant + addend) >> RND_SIZE;
+ /* half way: select even result */
+ if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1)))
+ a_mant &= ~1;
+ /* Note the rounding adds at least 1, so this is the maximum
+ value */
+ a_exp += a_mant >> (MANT_SIZE + 1);
+ if (a_mant <= MANT_MASK) {
+ /* denormalized or zero */
+ a_exp = 0;
+ } else if (a_exp >= EXP_MASK) {
+ /* overflow */
+ if (addend == 0) {
+ a_exp = EXP_MASK - 1;
+ a_mant = MANT_MASK;
+ } else {
+ /* infinity */
+ a_exp = EXP_MASK;
+ a_mant = 0;
+ }
+ *pfflags |= FFLAG_OVERFLOW | FFLAG_INEXACT;
+ }
+ return pack_sf(a_sign, a_exp, a_mant);
+}
+
+/* a_mant is considered to have at most F_SIZE - 1 bits */
+static F_UINT normalize_sf(uint32_t a_sign, int a_exp, F_UINT a_mant,
+ RoundingModeEnum rm, uint32_t *pfflags)
+{
+ int shift;
+ shift = clz(a_mant) - (F_SIZE - 1 - IMANT_SIZE);
+ assert(shift >= 0);
+ a_exp -= shift;
+ a_mant <<= shift;
+ return round_pack_sf(a_sign, a_exp, a_mant, rm, pfflags);
+}
+
+/* same as normalize_sf() but with a double word mantissa. a_mant1 is
+ considered to have at most F_SIZE - 1 bits */
+static F_UINT normalize2_sf(uint32_t a_sign, int a_exp, F_UINT a_mant1, F_UINT a_mant0,
+ RoundingModeEnum rm, uint32_t *pfflags)
+{
+ int l, shift;
+ if (a_mant1 == 0) {
+ l = F_SIZE + clz(a_mant0);
+ } else {
+ l = clz(a_mant1);
+ }
+ shift = l - (F_SIZE - 1 - IMANT_SIZE);
+ assert(shift >= 0);
+ a_exp -= shift;
+ if (shift == 0) {
+ a_mant1 |= (a_mant0 != 0);
+ } else if (shift < F_SIZE) {
+ a_mant1 = (a_mant1 << shift) | (a_mant0 >> (F_SIZE - shift));
+ a_mant0 <<= shift;
+ a_mant1 |= (a_mant0 != 0);
+ } else {
+ a_mant1 = a_mant0 << (shift - F_SIZE);
+ }
+ return round_pack_sf(a_sign, a_exp, a_mant1, rm, pfflags);
+}
+
+BOOL issignan_sf(F_UINT a)
+{
+ uint32_t a_exp1;
+ F_UINT a_mant;
+ a_exp1 = (a >> (MANT_SIZE - 1)) & ((1 << (EXP_SIZE + 1)) - 1);
+ a_mant = a & MANT_MASK;
+ return (a_exp1 == (2 * EXP_MASK) && a_mant != 0);
+}
+
+BOOL isnan_sf(F_UINT a)
+{
+ uint32_t a_exp;
+ F_UINT a_mant;
+ a_exp = (a >> MANT_SIZE) & EXP_MASK;
+ a_mant = a & MANT_MASK;
+ return (a_exp == EXP_MASK && a_mant != 0);
+}
+
+
+F_UINT add_sf(F_UINT a, F_UINT b, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ uint32_t a_sign, b_sign, a_exp, b_exp;
+ F_UINT tmp, a_mant, b_mant;
+
+ /* swap so that abs(a) >= abs(b) */
+ if ((a & ~SIGN_MASK) < (b & ~SIGN_MASK)) {
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+ a_sign = a >> (F_SIZE - 1);
+ b_sign = b >> (F_SIZE - 1);
+ a_exp = (a >> MANT_SIZE) & EXP_MASK;
+ b_exp = (b >> MANT_SIZE) & EXP_MASK;
+ a_mant = (a & MANT_MASK) << 3;
+ b_mant = (b & MANT_MASK) << 3;
+ if (unlikely(a_exp == EXP_MASK)) {
+ if (a_mant != 0) {
+ /* NaN result */
+ if (!(a_mant & (QNAN_MASK << 3)) || issignan_sf(b))
+ *pfflags |= FFLAG_INVALID_OP;
+ return F_QNAN;
+ } else if (b_exp == EXP_MASK && a_sign != b_sign) {
+ *pfflags |= FFLAG_INVALID_OP;
+ return F_QNAN;
+ } else {
+ /* infinity */
+ return a;
+ }
+ }
+ if (a_exp == 0) {
+ a_exp = 1;
+ } else {
+ a_mant |= (F_UINT)1 << (MANT_SIZE + 3);
+ }
+ if (b_exp == 0) {
+ b_exp = 1;
+ } else {
+ b_mant |= (F_UINT)1 << (MANT_SIZE + 3);
+ }
+ b_mant = rshift_rnd(b_mant, a_exp - b_exp);
+ if (a_sign == b_sign) {
+ /* same signs : add the absolute values */
+ a_mant += b_mant;
+ } else {
+ /* different signs : subtract the absolute values */
+ a_mant -= b_mant;
+ if (a_mant == 0) {
+ /* zero result : the sign needs a specific handling */
+ a_sign = (rm == RM_RDN);
+ }
+ }
+ a_exp += (RND_SIZE - 3);
+ return normalize_sf(a_sign, a_exp, a_mant, rm, pfflags);
+}
+
+F_UINT glue(sub_sf, F_SIZE)(F_UINT a, F_UINT b, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ return add_sf(a, b ^ SIGN_MASK, rm, pfflags);
+}
+
+static inline F_UINT normalize_subnormal_sf(int32_t *pa_exp, F_UINT a_mant)
+{
+ int shift;
+ shift = MANT_SIZE - ((F_SIZE - 1 - clz(a_mant)));
+ *pa_exp = 1 - shift;
+ return a_mant << shift;
+}
+
+#ifdef F_ULONG
+
+static F_UINT mul_u(F_UINT *plow, F_UINT a, F_UINT b)
+{
+ F_ULONG r;
+ r = (F_ULONG)a * (F_ULONG)b;
+ *plow = r;
+ return r >> F_SIZE;
+}
+
+#else
+
+#define FH_SIZE (F_SIZE / 2)
+
+static F_UINT mul_u(F_UINT *plow, F_UINT a, F_UINT b)
+{
+ F_UHALF a0, a1, b0, b1, r0, r1, r2, r3;
+ F_UINT r00, r01, r10, r11, c;
+ a0 = a;
+ a1 = a >> FH_SIZE;
+ b0 = b;
+ b1 = b >> FH_SIZE;
+
+ r00 = (F_UINT)a0 * (F_UINT)b0;
+ r01 = (F_UINT)a0 * (F_UINT)b1;
+ r10 = (F_UINT)a1 * (F_UINT)b0;
+ r11 = (F_UINT)a1 * (F_UINT)b1;
+
+ r0 = r00;
+ c = (r00 >> FH_SIZE) + (F_UHALF)r01 + (F_UHALF)r10;
+ r1 = c;
+ c = (c >> FH_SIZE) + (r01 >> FH_SIZE) + (r10 >> FH_SIZE) + (F_UHALF)r11;
+ r2 = c;
+ r3 = (c >> FH_SIZE) + (r11 >> FH_SIZE);
+
+ *plow = ((F_UINT)r1 << FH_SIZE) | r0;
+ return ((F_UINT)r3 << FH_SIZE) | r2;
+}
+
+#undef FH_SIZE
+
+#endif
+
+F_UINT mul_sf(F_UINT a, F_UINT b, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ uint32_t a_sign, b_sign, r_sign;
+ int32_t a_exp, b_exp, r_exp;
+ F_UINT a_mant, b_mant, r_mant, r_mant_low;
+
+ a_sign = a >> (F_SIZE - 1);
+ b_sign = b >> (F_SIZE - 1);
+ r_sign = a_sign ^ b_sign;
+ a_exp = (a >> MANT_SIZE) & EXP_MASK;
+ b_exp = (b >> MANT_SIZE) & EXP_MASK;
+ a_mant = a & MANT_MASK;
+ b_mant = b & MANT_MASK;
+ if (a_exp == EXP_MASK || b_exp == EXP_MASK) {
+ if (isnan_sf(a) || isnan_sf(b)) {
+ if (issignan_sf(a) || issignan_sf(b)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN;
+ } else {
+ /* infinity */
+ if ((a_exp == EXP_MASK && (b_exp == 0 && b_mant == 0)) ||
+ (b_exp == EXP_MASK && (a_exp == 0 && a_mant == 0))) {
+ *pfflags |= FFLAG_INVALID_OP;
+ return F_QNAN;
+ } else {
+ return pack_sf(r_sign, EXP_MASK, 0);
+ }
+ }
+ }
+ if (a_exp == 0) {
+ if (a_mant == 0)
+ return pack_sf(r_sign, 0, 0); /* zero */
+ a_mant = normalize_subnormal_sf(&a_exp, a_mant);
+ } else {
+ a_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ if (b_exp == 0) {
+ if (b_mant == 0)
+ return pack_sf(r_sign, 0, 0); /* zero */
+ b_mant = normalize_subnormal_sf(&b_exp, b_mant);
+ } else {
+ b_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ r_exp = a_exp + b_exp - (1 << (EXP_SIZE - 1)) + 2;
+
+ r_mant = mul_u(&r_mant_low,a_mant << RND_SIZE, b_mant << (RND_SIZE + 1));
+ r_mant |= (r_mant_low != 0);
+ return normalize_sf(r_sign, r_exp, r_mant, rm, pfflags);
+}
+
+/* fused multiply and add */
+F_UINT fma_sf(F_UINT a, F_UINT b, F_UINT c, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ uint32_t a_sign, b_sign, c_sign, r_sign;
+ int32_t a_exp, b_exp, c_exp, r_exp, shift;
+ F_UINT a_mant, b_mant, c_mant, r_mant1, r_mant0, c_mant1, c_mant0, mask;
+
+ a_sign = a >> (F_SIZE - 1);
+ b_sign = b >> (F_SIZE - 1);
+ c_sign = c >> (F_SIZE - 1);
+ r_sign = a_sign ^ b_sign;
+ a_exp = (a >> MANT_SIZE) & EXP_MASK;
+ b_exp = (b >> MANT_SIZE) & EXP_MASK;
+ c_exp = (c >> MANT_SIZE) & EXP_MASK;
+ a_mant = a & MANT_MASK;
+ b_mant = b & MANT_MASK;
+ c_mant = c & MANT_MASK;
+ if (a_exp == EXP_MASK || b_exp == EXP_MASK || c_exp == EXP_MASK) {
+ if (isnan_sf(a) || isnan_sf(b) || isnan_sf(c)) {
+ if (issignan_sf(a) || issignan_sf(b) || issignan_sf(c)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN;
+ } else {
+ /* infinities */
+ if ((a_exp == EXP_MASK && (b_exp == 0 && b_mant == 0)) ||
+ (b_exp == EXP_MASK && (a_exp == 0 && a_mant == 0)) ||
+ ((a_exp == EXP_MASK || b_exp == EXP_MASK) &&
+ (c_exp == EXP_MASK && r_sign != c_sign))) {
+ *pfflags |= FFLAG_INVALID_OP;
+ return F_QNAN;
+ } else if (c_exp == EXP_MASK) {
+ return pack_sf(c_sign, EXP_MASK, 0);
+ } else {
+ return pack_sf(r_sign, EXP_MASK, 0);
+ }
+ }
+ }
+ if (a_exp == 0) {
+ if (a_mant == 0)
+ goto mul_zero;
+ a_mant = normalize_subnormal_sf(&a_exp, a_mant);
+ } else {
+ a_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ if (b_exp == 0) {
+ if (b_mant == 0) {
+ mul_zero:
+ if (c_exp == 0 && c_mant == 0) {
+ if (c_sign != r_sign)
+ r_sign = (rm == RM_RDN);
+ return pack_sf(r_sign, 0, 0);
+ } else {
+ return c;
+ }
+ }
+ b_mant = normalize_subnormal_sf(&b_exp, b_mant);
+ } else {
+ b_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ /* multiply */
+ r_exp = a_exp + b_exp - (1 << (EXP_SIZE - 1)) + 3;
+
+ r_mant1 = mul_u(&r_mant0, a_mant << RND_SIZE, b_mant << RND_SIZE);
+ /* normalize to F_SIZE - 3 */
+ if (r_mant1 < ((F_UINT)1 << (F_SIZE - 3))) {
+ r_mant1 = (r_mant1 << 1) | (r_mant0 >> (F_SIZE - 1));
+ r_mant0 <<= 1;
+ r_exp--;
+ }
+
+ /* add */
+ if (c_exp == 0) {
+ if (c_mant == 0) {
+ /* add zero */
+ r_mant1 |= (r_mant0 != 0);
+ return normalize_sf(r_sign, r_exp, r_mant1, rm, pfflags);
+ }
+ c_mant = normalize_subnormal_sf(&c_exp, c_mant);
+ } else {
+ c_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ c_exp++;
+ c_mant1 = c_mant << (RND_SIZE - 1);
+ c_mant0 = 0;
+
+ // printf("r_s=%d r_exp=%d r_mant=%08x %08x\n", r_sign, r_exp, (uint32_t)r_mant1, (uint32_t)r_mant0);
+ // printf("c_s=%d c_exp=%d c_mant=%08x %08x\n", c_sign, c_exp, (uint32_t)c_mant1, (uint32_t)c_mant0);
+
+ /* ensure that abs(r) >= abs(c) */
+ if (!(r_exp > c_exp || (r_exp == c_exp && r_mant1 >= c_mant1))) {
+ F_UINT tmp;
+ int32_t c_tmp;
+ /* swap */
+ tmp = r_mant1; r_mant1 = c_mant1; c_mant1 = tmp;
+ tmp = r_mant0; r_mant0 = c_mant0; c_mant0 = tmp;
+ c_tmp = r_exp; r_exp = c_exp; c_exp = c_tmp;
+ c_tmp = r_sign; r_sign = c_sign; c_sign = c_tmp;
+ }
+ /* right shift c_mant */
+ shift = r_exp - c_exp;
+ if (shift >= 2 * F_SIZE) {
+ c_mant0 = (c_mant0 | c_mant1) != 0;
+ c_mant1 = 0;
+ } else if (shift >= F_SIZE + 1) {
+ c_mant0 = rshift_rnd(c_mant1, shift - F_SIZE);
+ c_mant1 = 0;
+ } else if (shift == F_SIZE) {
+ c_mant0 = c_mant1 | (c_mant0 != 0);
+ c_mant1 = 0;
+ } else if (shift != 0) {
+ mask = ((F_UINT)1 << shift) - 1;
+ c_mant0 = (c_mant1 << (F_SIZE - shift)) | (c_mant0 >> shift) | ((c_mant0 & mask) != 0);
+ c_mant1 = c_mant1 >> shift;
+ }
+ // printf(" r_mant=%08x %08x\n", (uint32_t)r_mant1, (uint32_t)r_mant0);
+ // printf(" c_mant=%08x %08x\n", (uint32_t)c_mant1, (uint32_t)c_mant0);
+ /* add or subtract */
+ if (r_sign == c_sign) {
+ r_mant0 += c_mant0;
+ r_mant1 += c_mant1 + (r_mant0 < c_mant0);
+ } else {
+ F_UINT tmp;
+ tmp = r_mant0;
+ r_mant0 -= c_mant0;
+ r_mant1 = r_mant1 - c_mant1 - (r_mant0 > tmp);
+ if ((r_mant0 | r_mant1) == 0) {
+ /* zero result : the sign needs a specific handling */
+ r_sign = (rm == RM_RDN);
+ }
+ }
+#if 0
+ // printf(" r1_mant=%08x %08x\n", (uint32_t)r_mant1, (uint32_t)r_mant0);
+ /* normalize */
+ if (r_mant1 == 0) {
+ r_mant1 = r_mant0;
+ r_exp -= F_SIZE;
+ } else {
+ shift = clz(r_mant1) - (F_SIZE - 1 - IMANT_SIZE);
+ if (shift != 0) {
+ r_mant1 = (r_mant1 << shift) | (r_mant0 >> (F_SIZE - shift));
+ r_mant0 <<= shift;
+ r_exp -= shift;
+ }
+ r_mant1 |= (r_mant0 != 0);
+ }
+ return normalize_sf(r_sign, r_exp, r_mant1, rm, pfflags);
+#else
+ return normalize2_sf(r_sign, r_exp, r_mant1, r_mant0, rm, pfflags);
+#endif
+}
+
+#ifdef F_ULONG
+
+static F_UINT divrem_u(F_UINT *pr, F_UINT ah, F_UINT al, F_UINT b)
+{
+ F_ULONG a;
+ a = ((F_ULONG)ah << F_SIZE) | al;
+ *pr = a % b;
+ return a / b;
+}
+
+#else
+
+/* XXX: optimize */
+static F_UINT divrem_u(F_UINT *pr, F_UINT a1, F_UINT a0, F_UINT b)
+{
+ int i, qb, ab;
+
+ assert(a1 < b);
+ for(i = 0; i < F_SIZE; i++) {
+ ab = a1 >> (F_SIZE - 1);
+ a1 = (a1 << 1) | (a0 >> (F_SIZE - 1));
+ if (ab || a1 >= b) {
+ a1 -= b;
+ qb = 1;
+ } else {
+ qb = 0;
+ }
+ a0 = (a0 << 1) | qb;
+ }
+ *pr = a1;
+ return a0;
+}
+
+#endif
+
+F_UINT div_sf(F_UINT a, F_UINT b, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ uint32_t a_sign, b_sign, r_sign;
+ int32_t a_exp, b_exp, r_exp;
+ F_UINT a_mant, b_mant, r_mant, r;
+
+ a_sign = a >> (F_SIZE - 1);
+ b_sign = b >> (F_SIZE - 1);
+ r_sign = a_sign ^ b_sign;
+ a_exp = (a >> MANT_SIZE) & EXP_MASK;
+ b_exp = (b >> MANT_SIZE) & EXP_MASK;
+ a_mant = a & MANT_MASK;
+ b_mant = b & MANT_MASK;
+ if (a_exp == EXP_MASK) {
+ if (a_mant != 0 || isnan_sf(b)) {
+ if (issignan_sf(a) || issignan_sf(b)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN;
+ } else if (b_exp == EXP_MASK) {
+ *pfflags |= FFLAG_INVALID_OP;
+ return F_QNAN;
+ } else {
+ return pack_sf(r_sign, EXP_MASK, 0);
+ }
+ } else if (b_exp == EXP_MASK) {
+ if (b_mant != 0) {
+ if (issignan_sf(a) || issignan_sf(b)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN;
+ } else {
+ return pack_sf(r_sign, 0, 0);
+ }
+ }
+
+ if (b_exp == 0) {
+ if (b_mant == 0) {
+ /* zero */
+ if (a_exp == 0 && a_mant == 0) {
+ *pfflags |= FFLAG_INVALID_OP;
+ return F_QNAN;
+ } else {
+ *pfflags |= FFLAG_DIVIDE_ZERO;
+ return pack_sf(r_sign, EXP_MASK, 0);
+ }
+ }
+ b_mant = normalize_subnormal_sf(&b_exp, b_mant);
+ } else {
+ b_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ if (a_exp == 0) {
+ if (a_mant == 0)
+ return pack_sf(r_sign, 0, 0); /* zero */
+ a_mant = normalize_subnormal_sf(&a_exp, a_mant);
+ } else {
+ a_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ r_exp = a_exp - b_exp + (1 << (EXP_SIZE - 1)) - 1;
+ r_mant = divrem_u(&r, a_mant, 0, b_mant << 2);
+ if (r != 0)
+ r_mant |= 1;
+ return normalize_sf(r_sign, r_exp, r_mant, rm, pfflags);
+}
+
+#ifdef F_ULONG
+
+/* compute sqrt(a) with a = ah*2^F_SIZE+al and a < 2^(F_SIZE - 2)
+ return true if not exact square. */
+static int sqrtrem_u(F_UINT *pr, F_UINT ah, F_UINT al)
+{
+ F_ULONG a, u, s;
+ int l, inexact;
+
+ /* 2^l >= a */
+ if (ah != 0) {
+ l = 2 * F_SIZE - clz(ah - 1);
+ } else {
+ if (al == 0) {
+ *pr = 0;
+ return 0;
+ }
+ l = F_SIZE - clz(al - 1);
+ }
+ a = ((F_ULONG)ah << F_SIZE) | al;
+ u = (F_ULONG)1 << ((l + 1) / 2);
+ for(;;) {
+ s = u;
+ u = ((a / s) + s) / 2;
+ if (u >= s)
+ break;
+ }
+ inexact = (a - s * s) != 0;
+ *pr = s;
+ return inexact;
+}
+
+#else
+
+static int sqrtrem_u(F_UINT *pr, F_UINT a1, F_UINT a0)
+{
+ int l, inexact;
+ F_UINT u, s, r, q, sq0, sq1;
+
+ /* 2^l >= a */
+ if (a1 != 0) {
+ l = 2 * F_SIZE - clz(a1 - 1);
+ } else {
+ if (a0 == 0) {
+ *pr = 0;
+ return 0;
+ }
+ l = F_SIZE - clz(a0 - 1);
+ }
+ u = (F_UINT)1 << ((l + 1) / 2);
+ for(;;) {
+ s = u;
+ q = divrem_u(&r, a1, a0, s);
+ u = (q + s) / 2;
+ if (u >= s)
+ break;
+ }
+ sq1 = mul_u(&sq0, s, s);
+ inexact = (sq0 != a0 || sq1 != a1);
+ *pr = s;
+ return inexact;
+}
+
+#endif
+
+F_UINT sqrt_sf(F_UINT a, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ uint32_t a_sign;
+ int32_t a_exp;
+ F_UINT a_mant;
+
+ a_sign = a >> (F_SIZE - 1);
+ a_exp = (a >> MANT_SIZE) & EXP_MASK;
+ a_mant = a & MANT_MASK;
+ if (a_exp == EXP_MASK) {
+ if (a_mant != 0) {
+ if (issignan_sf(a)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN;
+ } else if (a_sign) {
+ goto neg_error;
+ } else {
+ return a; /* +infinity */
+ }
+ }
+ if (a_sign) {
+ if (a_exp == 0 && a_mant == 0)
+ return a; /* -zero */
+ neg_error:
+ *pfflags |= FFLAG_INVALID_OP;
+ return F_QNAN;
+ }
+ if (a_exp == 0) {
+ if (a_mant == 0)
+ return pack_sf(0, 0, 0); /* zero */
+ a_mant = normalize_subnormal_sf(&a_exp, a_mant);
+ } else {
+ a_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ a_exp -= EXP_MASK / 2;
+ /* simpler to handle an even exponent */
+ if (a_exp & 1) {
+ a_exp--;
+ a_mant <<= 1;
+ }
+ a_exp = (a_exp >> 1) + EXP_MASK / 2;
+ a_mant <<= (F_SIZE - 4 - MANT_SIZE);
+ if (sqrtrem_u(&a_mant, a_mant, 0))
+ a_mant |= 1;
+ return normalize_sf(a_sign, a_exp, a_mant, rm, pfflags);
+}
+
+/* comparisons */
+
+static F_UINT glue(min_max_nan_sf, F_SIZE)(F_UINT a, F_UINT b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type)
+{
+ if (issignan_sf(a) || issignan_sf(b)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ if (minmax_type == FMINMAX_IEEE754_2008)
+ return F_QNAN;
+ }
+ if (minmax_type == FMINMAX_PROP) {
+ return F_QNAN;
+ } else {
+ if (isnan_sf(a)) {
+ if (isnan_sf(b))
+ return F_QNAN;
+ else
+ return b;
+ } else {
+ return a;
+ }
+ }
+}
+
+F_UINT glue(min_sf, F_SIZE)(F_UINT a, F_UINT b, uint32_t *pfflags,
+ SoftFPMinMaxTypeEnum minmax_type)
+{
+ uint32_t a_sign, b_sign;
+
+ if (isnan_sf(a) || isnan_sf(b)) {
+ return glue(min_max_nan_sf, F_SIZE)(a, b, pfflags, minmax_type);
+ }
+ a_sign = a >> (F_SIZE - 1);
+ b_sign = b >> (F_SIZE - 1);
+
+ if (a_sign != b_sign) {
+ if (a_sign)
+ return a;
+ else
+ return b;
+ } else {
+ if ((a < b) ^ a_sign)
+ return a;
+ else
+ return b;
+ }
+}
+
+F_UINT glue(max_sf, F_SIZE)(F_UINT a, F_UINT b, uint32_t *pfflags,
+ SoftFPMinMaxTypeEnum minmax_type)
+{
+ uint32_t a_sign, b_sign;
+
+ if (isnan_sf(a) || isnan_sf(b)) {
+ return glue(min_max_nan_sf, F_SIZE)(a, b, pfflags, minmax_type);
+ }
+ a_sign = a >> (F_SIZE - 1);
+ b_sign = b >> (F_SIZE - 1);
+
+ if (a_sign != b_sign) {
+ if (a_sign)
+ return b;
+ else
+ return a;
+ } else {
+ if ((a < b) ^ a_sign)
+ return b;
+ else
+ return a;
+ }
+}
+
+int glue(eq_quiet_sf, F_SIZE)(F_UINT a, F_UINT b, uint32_t *pfflags)
+{
+ if (isnan_sf(a) || isnan_sf(b)) {
+ if (issignan_sf(a) || issignan_sf(b)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return 0;
+ }
+
+ if ((F_UINT)((a | b) << 1) == 0)
+ return 1; /* zero case */
+ return (a == b);
+}
+
+int glue(le_sf, F_SIZE)(F_UINT a, F_UINT b, uint32_t *pfflags)
+{
+ uint32_t a_sign, b_sign;
+
+ if (isnan_sf(a) || isnan_sf(b)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ return 0;
+ }
+
+ a_sign = a >> (F_SIZE - 1);
+ b_sign = b >> (F_SIZE - 1);
+ if (a_sign != b_sign) {
+ return (a_sign || ((F_UINT)((a | b) << 1) == 0));
+ } else {
+ if (a_sign) {
+ return (a >= b);
+ } else {
+ return (a <= b);
+ }
+ }
+}
+
+int glue(lt_sf, F_SIZE)(F_UINT a, F_UINT b, uint32_t *pfflags)
+{
+ uint32_t a_sign, b_sign;
+
+ if (isnan_sf(a) || isnan_sf(b)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ return 0;
+ }
+
+ a_sign = a >> (F_SIZE - 1);
+ b_sign = b >> (F_SIZE - 1);
+ if (a_sign != b_sign) {
+ return (a_sign && ((F_UINT)((a | b) << 1) != 0));
+ } else {
+ if (a_sign) {
+ return (a > b);
+ } else {
+ return (a < b);
+ }
+ }
+}
+
+uint32_t glue(fclass_sf, F_SIZE)(F_UINT a)
+{
+ uint32_t a_sign;
+ int32_t a_exp;
+ F_UINT a_mant;
+ uint32_t ret;
+
+ a_sign = a >> (F_SIZE - 1);
+ a_exp = (a >> MANT_SIZE) & EXP_MASK;
+ a_mant = a & MANT_MASK;
+ if (a_exp == EXP_MASK) {
+ if (a_mant != 0) {
+ if (a_mant & QNAN_MASK)
+ ret = FCLASS_QNAN;
+ else
+ ret = FCLASS_SNAN;
+ } else {
+ if (a_sign)
+ ret = FCLASS_NINF;
+ else
+ ret = FCLASS_PINF;
+ }
+ } else if (a_exp == 0) {
+ if (a_mant == 0) {
+ if (a_sign)
+ ret = FCLASS_NZERO;
+ else
+ ret = FCLASS_PZERO;
+ } else {
+ if (a_sign)
+ ret = FCLASS_NSUBNORMAL;
+ else
+ ret = FCLASS_PSUBNORMAL;
+ }
+ } else {
+ if (a_sign)
+ ret = FCLASS_NNORMAL;
+ else
+ ret = FCLASS_PNORMAL;
+ }
+ return ret;
+}
+
+/* conversions between floats */
+
+#if F_SIZE >= 64
+
+F_UINT cvt_sf32_sf(uint32_t a, uint32_t *pfflags)
+{
+ uint32_t a_sign;
+ int32_t a_exp;
+ F_UINT a_mant;
+
+ a_mant = unpack_sf32(&a_sign, &a_exp, a);
+ if (a_exp == 0xff) {
+ if (a_mant != 0) {
+ /* NaN */
+ if (issignan_sf32(a)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN;
+ } else {
+ /* infinity */
+ return pack_sf(a_sign, EXP_MASK, 0);
+ }
+ }
+ if (a_exp == 0) {
+ if (a_mant == 0)
+ return pack_sf(a_sign, 0, 0); /* zero */
+ a_mant = normalize_subnormal_sf32(&a_exp, a_mant);
+ }
+ /* convert the exponent value */
+ a_exp = a_exp - 0x7f + (EXP_MASK / 2);
+ /* shift the mantissa */
+ a_mant <<= (MANT_SIZE - 23);
+ /* We assume the target float is large enough to that no
+ normalization is necessary */
+ return pack_sf(a_sign, a_exp, a_mant);
+}
+
+uint32_t glue(glue(cvt_sf, F_SIZE), _sf32)(F_UINT a, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ uint32_t a_sign;
+ int32_t a_exp;
+ F_UINT a_mant;
+
+ a_mant = unpack_sf(&a_sign, &a_exp, a);
+ if (a_exp == EXP_MASK) {
+ if (a_mant != 0) {
+ /* NaN */
+ if (issignan_sf(a)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN32;
+ } else {
+ /* infinity */
+ return pack_sf32(a_sign, 0xff, 0);
+ }
+ }
+ if (a_exp == 0) {
+ if (a_mant == 0)
+ return pack_sf32(a_sign, 0, 0); /* zero */
+ normalize_subnormal_sf(&a_exp, a_mant);
+ } else {
+ a_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ /* convert the exponent value */
+ a_exp = a_exp - (EXP_MASK / 2) + 0x7f;
+ /* shift the mantissa */
+ a_mant = rshift_rnd(a_mant, MANT_SIZE - (32 - 2));
+ return normalize_sf32(a_sign, a_exp, a_mant, rm, pfflags);
+}
+
+#endif
+
+#if F_SIZE >= 128
+
+F_UINT cvt_sf64_sf(uint64_t a, uint32_t *pfflags)
+{
+ uint32_t a_sign;
+ int32_t a_exp;
+ F_UINT a_mant;
+
+ a_mant = unpack_sf64(&a_sign, &a_exp, a);
+
+ if (a_exp == 0x7ff) {
+ if (a_mant != 0) {
+ /* NaN */
+ if (issignan_sf64(a)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN;
+ } else {
+ /* infinity */
+ return pack_sf(a_sign, EXP_MASK, 0);
+ }
+ }
+ if (a_exp == 0) {
+ if (a_mant == 0)
+ return pack_sf(a_sign, 0, 0); /* zero */
+ a_mant = normalize_subnormal_sf64(&a_exp, a_mant);
+ }
+ /* convert the exponent value */
+ a_exp = a_exp - 0x3ff + (EXP_MASK / 2);
+ /* shift the mantissa */
+ a_mant <<= (MANT_SIZE - 52);
+ return pack_sf(a_sign, a_exp, a_mant);
+}
+
+uint64_t glue(glue(cvt_sf, F_SIZE), _sf64)(F_UINT a, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ uint32_t a_sign;
+ int32_t a_exp;
+ F_UINT a_mant;
+
+ a_mant = unpack_sf(&a_sign, &a_exp, a);
+ if (a_exp == EXP_MASK) {
+ if (a_mant != 0) {
+ /* NaN */
+ if (issignan_sf(a)) {
+ *pfflags |= FFLAG_INVALID_OP;
+ }
+ return F_QNAN64;
+ } else {
+ /* infinity */
+ return pack_sf64(a_sign, 0x7ff, 0);
+ }
+ }
+ if (a_exp == 0) {
+ if (a_mant == 0)
+ return pack_sf64(a_sign, 0, 0); /* zero */
+ normalize_subnormal_sf(&a_exp, a_mant);
+ } else {
+ a_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ /* convert the exponent value */
+ a_exp = a_exp - (EXP_MASK / 2) + 0x3ff;
+ /* shift the mantissa */
+ a_mant = rshift_rnd(a_mant, MANT_SIZE - (64 - 2));
+ return normalize_sf64(a_sign, a_exp, a_mant, rm, pfflags);
+}
+
+#endif
+
+#undef clz
+
+#define ICVT_SIZE 32
+#include "softfp_template_icvt.h"
+
+#define ICVT_SIZE 64
+#include "softfp_template_icvt.h"
+
+#ifdef HAVE_INT128
+#define ICVT_SIZE 128
+#include "softfp_template_icvt.h"
+#endif
+
+#undef F_SIZE
+#undef F_UINT
+#undef F_ULONG
+#undef F_UHALF
+#undef MANT_SIZE
+#undef EXP_SIZE
+#undef EXP_MASK
+#undef MANT_MASK
+#undef SIGN_MASK
+#undef IMANT_SIZE
+#undef RND_SIZE
+#undef QNAN_MASK
+#undef F_QNAN
+
+#undef pack_sf
+#undef unpack_sf
+#undef rshift_rnd
+#undef round_pack_sf
+#undef normalize_sf
+#undef normalize2_sf
+#undef issignan_sf
+#undef isnan_sf
+#undef add_sf
+#undef mul_sf
+#undef fma_sf
+#undef div_sf
+#undef sqrt_sf
+#undef normalize_subnormal_sf
+#undef divrem_u
+#undef sqrtrem_u
+#undef mul_u
+#undef cvt_sf32_sf
+#undef cvt_sf64_sf
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/softfp_template_icvt.h b/jslinux-2019-12-21/tinyemu-2019-12-21/softfp_template_icvt.h
new file mode 100644
index 0000000..9360ac1
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/softfp_template_icvt.h
@@ -0,0 +1,171 @@
+/*
+ * SoftFP Library
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#if ICVT_SIZE == 32
+#define ICVT_UINT uint32_t
+#define ICVT_INT int32_t
+#elif ICVT_SIZE == 64
+#define ICVT_UINT uint64_t
+#define ICVT_INT int64_t
+#elif ICVT_SIZE == 128
+#define ICVT_UINT uint128_t
+#define ICVT_INT int128_t
+#else
+#error unsupported icvt
+#endif
+
+/* conversions between float and integers */
+static ICVT_INT glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
+ uint32_t *pfflags, BOOL is_unsigned)
+{
+ uint32_t a_sign, addend, rnd_bits;
+ int32_t a_exp;
+ F_UINT a_mant;
+ ICVT_UINT r, r_max;
+
+ a_sign = a >> (F_SIZE - 1);
+ a_exp = (a >> MANT_SIZE) & EXP_MASK;
+ a_mant = a & MANT_MASK;
+ if (a_exp == EXP_MASK && a_mant != 0)
+ a_sign = 0; /* NaN is like +infinity */
+ if (a_exp == 0) {
+ a_exp = 1;
+ } else {
+ a_mant |= (F_UINT)1 << MANT_SIZE;
+ }
+ a_mant <<= RND_SIZE;
+ a_exp = a_exp - (EXP_MASK / 2) - MANT_SIZE;
+
+ if (is_unsigned)
+ r_max = (ICVT_UINT)a_sign - 1;
+ else
+ r_max = ((ICVT_UINT)1 << (ICVT_SIZE - 1)) - (ICVT_UINT)(a_sign ^ 1);
+ if (a_exp >= 0) {
+ if (a_exp <= (ICVT_SIZE - 1 - MANT_SIZE)) {
+ r = (ICVT_UINT)(a_mant >> RND_SIZE) << a_exp;
+ if (r > r_max)
+ goto overflow;
+ } else {
+ overflow:
+ *pfflags |= FFLAG_INVALID_OP;
+ return r_max;
+ }
+ } else {
+ a_mant = rshift_rnd(a_mant, -a_exp);
+
+ switch(rm) {
+ case RM_RNE:
+ case RM_RMM:
+ addend = (1 << (RND_SIZE - 1));
+ break;
+ case RM_RTZ:
+ addend = 0;
+ break;
+ default:
+ case RM_RDN:
+ case RM_RUP:
+ if (a_sign ^ (rm & 1))
+ addend = (1 << RND_SIZE) - 1;
+ else
+ addend = 0;
+ break;
+ }
+
+ rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);
+ a_mant = (a_mant + addend) >> RND_SIZE;
+ /* half way: select even result */
+ if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1)))
+ a_mant &= ~1;
+ if (a_mant > r_max)
+ goto overflow;
+ r = a_mant;
+ if (rnd_bits != 0)
+ *pfflags |= FFLAG_INEXACT;
+ }
+ if (a_sign)
+ r = -r;
+ return r;
+}
+
+ICVT_INT glue(glue(glue(cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(a, rm,
+ pfflags, FALSE);
+}
+
+ICVT_UINT glue(glue(glue(cvt_sf, F_SIZE), _u), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE) (a, rm,
+ pfflags, TRUE);
+}
+
+/* conversions between float and integers */
+static F_UINT glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a,
+ RoundingModeEnum rm,
+ uint32_t *pfflags,
+ BOOL is_unsigned)
+{
+ uint32_t a_sign;
+ int32_t a_exp;
+ F_UINT a_mant;
+ ICVT_UINT r, mask;
+ int l;
+
+ if (!is_unsigned && a < 0) {
+ a_sign = 1;
+ r = -(ICVT_UINT)a;
+ } else {
+ a_sign = 0;
+ r = a;
+ }
+ a_exp = (EXP_MASK / 2) + F_SIZE - 2;
+ /* need to reduce range before generic float normalization */
+ l = ICVT_SIZE - glue(clz, ICVT_SIZE)(r) - (F_SIZE - 1);
+ if (l > 0) {
+ mask = r & (((ICVT_UINT)1 << l) - 1);
+ r = (r >> l) | ((r & mask) != 0);
+ a_exp += l;
+ }
+ a_mant = r;
+ return normalize_sf(a_sign, a_exp, a_mant, rm, pfflags);
+}
+
+F_UINT glue(glue(glue(cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a,
+ RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, pfflags, FALSE);
+}
+
+F_UINT glue(glue(glue(cvt_u, ICVT_SIZE), _sf), F_SIZE)(ICVT_UINT a,
+ RoundingModeEnum rm,
+ uint32_t *pfflags)
+{
+ return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, pfflags, TRUE);
+}
+
+#undef ICVT_SIZE
+#undef ICVT_INT
+#undef ICVT_UINT
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg b/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg
new file mode 100755
index 0000000..8c2f617
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg
Binary files differ
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.c b/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.c
new file mode 100644
index 0000000..3271290
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.c
@@ -0,0 +1,102 @@
+/*
+ * Disk image splitter
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+
+int main(int argc, char **argv)
+{
+ int blocksize, ret, i;
+ const char *infilename, *outpath;
+ FILE *f, *fo;
+ char buf1[1024];
+ uint8_t *buf;
+
+ if ((optind + 1) >= argc) {
+ printf("splitimg version " CONFIG_VERSION ", Copyright (c) 2011-2016 Fabrice Bellard\n"
+ "usage: splitimg infile outpath [blocksize]\n"
+ "Create a multi-file disk image for the RISCVEMU HTTP block device\n"
+ "\n"
+ "outpath must be a directory\n"
+ "blocksize is the block size in KB\n");
+ exit(1);
+ }
+
+ infilename = argv[optind++];
+ outpath = argv[optind++];
+ blocksize = 256;
+ if (optind < argc)
+ blocksize = strtol(argv[optind++], NULL, 0);
+
+ blocksize *= 1024;
+
+ buf = malloc(blocksize);
+
+ f = fopen(infilename, "rb");
+ if (!f) {
+ perror(infilename);
+ exit(1);
+ }
+ i = 0;
+ for(;;) {
+ ret = fread(buf, 1, blocksize, f);
+ if (ret < 0) {
+ perror("fread");
+ exit(1);
+ }
+ if (ret == 0)
+ break;
+ if (ret < blocksize) {
+ printf("warning: last block is not full\n");
+ memset(buf + ret, 0, blocksize - ret);
+ }
+ snprintf(buf1, sizeof(buf1), "%s/blk%09u.bin", outpath, i);
+ fo = fopen(buf1, "wb");
+ if (!fo) {
+ perror(buf1);
+ exit(1);
+ }
+ fwrite(buf, 1, blocksize, fo);
+ fclose(fo);
+ i++;
+ }
+ fclose(f);
+ printf("%d blocks\n", i);
+
+ snprintf(buf1, sizeof(buf1), "%s/blk.txt", outpath);
+ fo = fopen(buf1, "wb");
+ if (!fo) {
+ perror(buf1);
+ exit(1);
+ }
+ fprintf(fo, "{\n");
+ fprintf(fo, " block_size: %d,\n", blocksize / 1024);
+ fprintf(fo, " n_block: %d,\n", i);
+ fprintf(fo, "}\n");
+ fclose(fo);
+ return 0;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.d b/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.d
new file mode 100644
index 0000000..dddfe8d
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.d
@@ -0,0 +1 @@
+splitimg.o: splitimg.c
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.o b/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.o
new file mode 100644
index 0000000..5b15ee4
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/splitimg.o
Binary files differ
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/temu.c b/jslinux-2019-12-21/tinyemu-2019-12-21/temu.c
new file mode 100644
index 0000000..7c07f3b
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/temu.c
@@ -0,0 +1,835 @@
+/*
+ * TinyEMU
+ *
+ * Copyright (c) 2016-2018 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#ifndef _WIN32
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#endif
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "virtio.h"
+#include "machine.h"
+#ifdef CONFIG_FS_NET
+#include "fs_utils.h"
+#include "fs_wget.h"
+#endif
+#ifdef CONFIG_SLIRP
+#include "slirp/libslirp.h"
+#endif
+
+#ifndef _WIN32
+
+typedef struct {
+ int stdin_fd;
+ int console_esc_state;
+ BOOL resize_pending;
+} STDIODevice;
+
+static struct termios oldtty;
+static int old_fd0_flags;
+static STDIODevice *global_stdio_device;
+
+static void term_exit(void)
+{
+ tcsetattr (0, TCSANOW, &oldtty);
+ fcntl(0, F_SETFL, old_fd0_flags);
+}
+
+static void term_init(BOOL allow_ctrlc)
+{
+ struct termios tty;
+
+ memset(&tty, 0, sizeof(tty));
+ tcgetattr (0, &tty);
+ oldtty = tty;
+ old_fd0_flags = fcntl(0, F_GETFL);
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ if (!allow_ctrlc)
+ tty.c_lflag &= ~ISIG;
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 0;
+
+ tcsetattr (0, TCSANOW, &tty);
+
+ atexit(term_exit);
+}
+
+static void console_write(void *opaque, const uint8_t *buf, int len)
+{
+ fwrite(buf, 1, len, stdout);
+ fflush(stdout);
+}
+
+static int console_read(void *opaque, uint8_t *buf, int len)
+{
+ STDIODevice *s = opaque;
+ int ret, i, j;
+ uint8_t ch;
+
+ if (len <= 0)
+ return 0;
+
+ ret = read(s->stdin_fd, buf, len);
+ if (ret < 0)
+ return 0;
+ if (ret == 0) {
+ /* EOF */
+ exit(1);
+ }
+
+ j = 0;
+ for(i = 0; i < ret; i++) {
+ ch = buf[i];
+ if (s->console_esc_state) {
+ s->console_esc_state = 0;
+ switch(ch) {
+ case 'x':
+ printf("Terminated\n");
+ exit(0);
+ case 'h':
+ printf("\n"
+ "C-a h print this help\n"
+ "C-a x exit emulator\n"
+ "C-a C-a send C-a\n"
+ );
+ break;
+ case 1:
+ goto output_char;
+ default:
+ break;
+ }
+ } else {
+ if (ch == 1) {
+ s->console_esc_state = 1;
+ } else {
+ output_char:
+ buf[j++] = ch;
+ }
+ }
+ }
+ return j;
+}
+
+static void term_resize_handler(int sig)
+{
+ if (global_stdio_device)
+ global_stdio_device->resize_pending = TRUE;
+}
+
+static void console_get_size(STDIODevice *s, int *pw, int *ph)
+{
+ struct winsize ws;
+ int width, height;
+ /* default values */
+ width = 80;
+ height = 25;
+ if (ioctl(s->stdin_fd, TIOCGWINSZ, &ws) == 0 &&
+ ws.ws_col >= 4 && ws.ws_row >= 4) {
+ width = ws.ws_col;
+ height = ws.ws_row;
+ }
+ *pw = width;
+ *ph = height;
+}
+
+CharacterDevice *console_init(BOOL allow_ctrlc)
+{
+ CharacterDevice *dev;
+ STDIODevice *s;
+ struct sigaction sig;
+
+ term_init(allow_ctrlc);
+
+ dev = mallocz(sizeof(*dev));
+ s = mallocz(sizeof(*s));
+ s->stdin_fd = 0;
+ /* Note: the glibc does not properly tests the return value of
+ write() in printf, so some messages on stdout may be lost */
+ fcntl(s->stdin_fd, F_SETFL, O_NONBLOCK);
+
+ s->resize_pending = TRUE;
+ global_stdio_device = s;
+
+ /* use a signal to get the host terminal resize events */
+ sig.sa_handler = term_resize_handler;
+ sigemptyset(&sig.sa_mask);
+ sig.sa_flags = 0;
+ sigaction(SIGWINCH, &sig, NULL);
+
+ dev->opaque = s;
+ dev->write_data = console_write;
+ dev->read_data = console_read;
+ return dev;
+}
+
+#endif /* !_WIN32 */
+
+typedef enum {
+ BF_MODE_RO,
+ BF_MODE_RW,
+ BF_MODE_SNAPSHOT,
+} BlockDeviceModeEnum;
+
+#define SECTOR_SIZE 512
+
+typedef struct BlockDeviceFile {
+ FILE *f;
+ int64_t nb_sectors;
+ BlockDeviceModeEnum mode;
+ uint8_t **sector_table;
+} BlockDeviceFile;
+
+static int64_t bf_get_sector_count(BlockDevice *bs)
+{
+ BlockDeviceFile *bf = bs->opaque;
+ return bf->nb_sectors;
+}
+
+//#define DUMP_BLOCK_READ
+
+static int bf_read_async(BlockDevice *bs,
+ uint64_t sector_num, uint8_t *buf, int n,
+ BlockDeviceCompletionFunc *cb, void *opaque)
+{
+ BlockDeviceFile *bf = bs->opaque;
+ // printf("bf_read_async: sector_num=%" PRId64 " n=%d\n", sector_num, n);
+#ifdef DUMP_BLOCK_READ
+ {
+ static FILE *f;
+ if (!f)
+ f = fopen("/tmp/read_sect.txt", "wb");
+ fprintf(f, "%" PRId64 " %d\n", sector_num, n);
+ }
+#endif
+ if (!bf->f)
+ return -1;
+ if (bf->mode == BF_MODE_SNAPSHOT) {
+ int i;
+ for(i = 0; i < n; i++) {
+ if (!bf->sector_table[sector_num]) {
+ fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET);
+ fread(buf, 1, SECTOR_SIZE, bf->f);
+ } else {
+ memcpy(buf, bf->sector_table[sector_num], SECTOR_SIZE);
+ }
+ sector_num++;
+ buf += SECTOR_SIZE;
+ }
+ } else {
+ fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET);
+ fread(buf, 1, n * SECTOR_SIZE, bf->f);
+ }
+ /* synchronous read */
+ return 0;
+}
+
+static int bf_write_async(BlockDevice *bs,
+ uint64_t sector_num, const uint8_t *buf, int n,
+ BlockDeviceCompletionFunc *cb, void *opaque)
+{
+ BlockDeviceFile *bf = bs->opaque;
+ int ret;
+
+ switch(bf->mode) {
+ case BF_MODE_RO:
+ ret = -1; /* error */
+ break;
+ case BF_MODE_RW:
+ fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET);
+ fwrite(buf, 1, n * SECTOR_SIZE, bf->f);
+ ret = 0;
+ break;
+ case BF_MODE_SNAPSHOT:
+ {
+ int i;
+ if ((sector_num + n) > bf->nb_sectors)
+ return -1;
+ for(i = 0; i < n; i++) {
+ if (!bf->sector_table[sector_num]) {
+ bf->sector_table[sector_num] = malloc(SECTOR_SIZE);
+ }
+ memcpy(bf->sector_table[sector_num], buf, SECTOR_SIZE);
+ sector_num++;
+ buf += SECTOR_SIZE;
+ }
+ ret = 0;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ return ret;
+}
+
+static BlockDevice *block_device_init(const char *filename,
+ BlockDeviceModeEnum mode)
+{
+ BlockDevice *bs;
+ BlockDeviceFile *bf;
+ int64_t file_size;
+ FILE *f;
+ const char *mode_str;
+
+ if (mode == BF_MODE_RW) {
+ mode_str = "r+b";
+ } else {
+ mode_str = "rb";
+ }
+
+ f = fopen(filename, mode_str);
+ if (!f) {
+ perror(filename);
+ exit(1);
+ }
+ fseek(f, 0, SEEK_END);
+ file_size = ftello(f);
+
+ bs = mallocz(sizeof(*bs));
+ bf = mallocz(sizeof(*bf));
+
+ bf->mode = mode;
+ bf->nb_sectors = file_size / 512;
+ bf->f = f;
+
+ if (mode == BF_MODE_SNAPSHOT) {
+ bf->sector_table = mallocz(sizeof(bf->sector_table[0]) *
+ bf->nb_sectors);
+ }
+
+ bs->opaque = bf;
+ bs->get_sector_count = bf_get_sector_count;
+ bs->read_async = bf_read_async;
+ bs->write_async = bf_write_async;
+ return bs;
+}
+
+#ifndef _WIN32
+
+typedef struct {
+ int fd;
+ BOOL select_filled;
+} TunState;
+
+static void tun_write_packet(EthernetDevice *net,
+ const uint8_t *buf, int len)
+{
+ TunState *s = net->opaque;
+ write(s->fd, buf, len);
+}
+
+static void tun_select_fill(EthernetDevice *net, int *pfd_max,
+ fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int *pdelay)
+{
+ TunState *s = net->opaque;
+ int net_fd = s->fd;
+
+ s->select_filled = net->device_can_write_packet(net);
+ if (s->select_filled) {
+ FD_SET(net_fd, rfds);
+ *pfd_max = max_int(*pfd_max, net_fd);
+ }
+}
+
+static void tun_select_poll(EthernetDevice *net,
+ fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int select_ret)
+{
+ TunState *s = net->opaque;
+ int net_fd = s->fd;
+ uint8_t buf[2048];
+ int ret;
+
+ if (select_ret <= 0)
+ return;
+ if (s->select_filled && FD_ISSET(net_fd, rfds)) {
+ ret = read(net_fd, buf, sizeof(buf));
+ if (ret > 0)
+ net->device_write_packet(net, buf, ret);
+ }
+
+}
+
+/* configure with:
+# bridge configuration (connect tap0 to bridge interface br0)
+ ip link add br0 type bridge
+ ip tuntap add dev tap0 mode tap [user x] [group x]
+ ip link set tap0 master br0
+ ip link set dev br0 up
+ ip link set dev tap0 up
+
+# NAT configuration (eth1 is the interface connected to internet)
+ ifconfig br0 192.168.3.1
+ echo 1 > /proc/sys/net/ipv4/ip_forward
+ iptables -D FORWARD 1
+ iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
+
+ In the VM:
+ ifconfig eth0 192.168.3.2
+ route add -net 0.0.0.0 netmask 0.0.0.0 gw 192.168.3.1
+*/
+static EthernetDevice *tun_open(const char *ifname)
+{
+ struct ifreq ifr;
+ int fd, ret;
+ EthernetDevice *net;
+ TunState *s;
+
+ fd = open("/dev/net/tun", O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "Error: could not open /dev/net/tun\n");
+ return NULL;
+ }
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ pstrcpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
+ ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (ret != 0) {
+ fprintf(stderr, "Error: could not configure /dev/net/tun\n");
+ close(fd);
+ return NULL;
+ }
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ net = mallocz(sizeof(*net));
+ net->mac_addr[0] = 0x02;
+ net->mac_addr[1] = 0x00;
+ net->mac_addr[2] = 0x00;
+ net->mac_addr[3] = 0x00;
+ net->mac_addr[4] = 0x00;
+ net->mac_addr[5] = 0x01;
+ s = mallocz(sizeof(*s));
+ s->fd = fd;
+ net->opaque = s;
+ net->write_packet = tun_write_packet;
+ net->select_fill = tun_select_fill;
+ net->select_poll = tun_select_poll;
+ return net;
+}
+
+#endif /* !_WIN32 */
+
+#ifdef CONFIG_SLIRP
+
+/*******************************************************/
+/* slirp */
+
+static Slirp *slirp_state;
+
+static void slirp_write_packet(EthernetDevice *net,
+ const uint8_t *buf, int len)
+{
+ Slirp *slirp_state = net->opaque;
+ slirp_input(slirp_state, buf, len);
+}
+
+int slirp_can_output(void *opaque)
+{
+ EthernetDevice *net = opaque;
+ return net->device_can_write_packet(net);
+}
+
+void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
+{
+ EthernetDevice *net = opaque;
+ return net->device_write_packet(net, pkt, pkt_len);
+}
+
+static void slirp_select_fill1(EthernetDevice *net, int *pfd_max,
+ fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int *pdelay)
+{
+ Slirp *slirp_state = net->opaque;
+ slirp_select_fill(slirp_state, pfd_max, rfds, wfds, efds);
+}
+
+static void slirp_select_poll1(EthernetDevice *net,
+ fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int select_ret)
+{
+ Slirp *slirp_state = net->opaque;
+ slirp_select_poll(slirp_state, rfds, wfds, efds, (select_ret <= 0));
+}
+
+static EthernetDevice *slirp_open(void)
+{
+ EthernetDevice *net;
+ struct in_addr net_addr = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
+ struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
+ struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
+ struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
+ struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
+ const char *bootfile = NULL;
+ const char *vhostname = NULL;
+ int restricted = 0;
+
+ if (slirp_state) {
+ fprintf(stderr, "Only a single slirp instance is allowed\n");
+ return NULL;
+ }
+ net = mallocz(sizeof(*net));
+
+ slirp_state = slirp_init(restricted, net_addr, mask, host, vhostname,
+ "", bootfile, dhcp, dns, net);
+
+ net->mac_addr[0] = 0x02;
+ net->mac_addr[1] = 0x00;
+ net->mac_addr[2] = 0x00;
+ net->mac_addr[3] = 0x00;
+ net->mac_addr[4] = 0x00;
+ net->mac_addr[5] = 0x01;
+ net->opaque = slirp_state;
+ net->write_packet = slirp_write_packet;
+ net->select_fill = slirp_select_fill1;
+ net->select_poll = slirp_select_poll1;
+
+ return net;
+}
+
+#endif /* CONFIG_SLIRP */
+
+#define MAX_EXEC_CYCLE 500000
+#define MAX_SLEEP_TIME 10 /* in ms */
+
+void virt_machine_run(VirtMachine *m)
+{
+ fd_set rfds, wfds, efds;
+ int fd_max, ret, delay;
+ struct timeval tv;
+#ifndef _WIN32
+ int stdin_fd;
+#endif
+
+ delay = virt_machine_get_sleep_duration(m, MAX_SLEEP_TIME);
+
+ /* wait for an event */
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+ fd_max = -1;
+#ifndef _WIN32
+ if (m->console_dev && virtio_console_can_write_data(m->console_dev)) {
+ STDIODevice *s = m->console->opaque;
+ stdin_fd = s->stdin_fd;
+ FD_SET(stdin_fd, &rfds);
+ fd_max = stdin_fd;
+
+ if (s->resize_pending) {
+ int width, height;
+ console_get_size(s, &width, &height);
+ virtio_console_resize_event(m->console_dev, width, height);
+ s->resize_pending = FALSE;
+ }
+ }
+#endif
+ if (m->net) {
+ m->net->select_fill(m->net, &fd_max, &rfds, &wfds, &efds, &delay);
+ }
+#ifdef CONFIG_FS_NET
+ fs_net_set_fdset(&fd_max, &rfds, &wfds, &efds, &delay);
+#endif
+ tv.tv_sec = delay / 1000;
+ tv.tv_usec = (delay % 1000) * 1000;
+ ret = select(fd_max + 1, &rfds, &wfds, &efds, &tv);
+ if (m->net) {
+ m->net->select_poll(m->net, &rfds, &wfds, &efds, ret);
+ }
+ if (ret > 0) {
+#ifndef _WIN32
+ if (m->console_dev && FD_ISSET(stdin_fd, &rfds)) {
+ uint8_t buf[128];
+ int ret, len;
+ len = virtio_console_get_write_len(m->console_dev);
+ len = min_int(len, sizeof(buf));
+ ret = m->console->read_data(m->console->opaque, buf, len);
+ if (ret > 0) {
+ virtio_console_write_data(m->console_dev, buf, ret);
+ }
+ }
+#endif
+ }
+
+#ifdef CONFIG_SDL
+ sdl_refresh(m);
+#endif
+
+ virt_machine_interp(m, MAX_EXEC_CYCLE);
+}
+
+/*******************************************************/
+
+static struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "ctrlc", no_argument },
+ { "rw", no_argument },
+ { "ro", no_argument },
+ { "append", required_argument },
+ { "no-accel", no_argument },
+ { "build-preload", required_argument },
+ { NULL },
+};
+
+void help(void)
+{
+ printf("temu version " CONFIG_VERSION ", Copyright (c) 2016-2018 Fabrice Bellard\n"
+ "usage: riscvemu [options] config_file\n"
+ "options are:\n"
+ "-m ram_size set the RAM size in MB\n"
+ "-rw allow write access to the disk image (default=snapshot)\n"
+ "-ctrlc the C-c key stops the emulator instead of being sent to the\n"
+ " emulated software\n"
+ "-append cmdline append cmdline to the kernel command line\n"
+ "-no-accel disable VM acceleration (KVM, x86 machine only)\n"
+ "\n"
+ "Console keys:\n"
+ "Press C-a x to exit the emulator, C-a h to get some help.\n");
+ exit(1);
+}
+
+#ifdef CONFIG_FS_NET
+static BOOL net_completed;
+
+static void net_start_cb(void *arg)
+{
+ net_completed = TRUE;
+}
+
+static BOOL net_poll_cb(void *arg)
+{
+ return net_completed;
+}
+
+#endif
+
+int main(int argc, char **argv)
+{
+ VirtMachine *s;
+ const char *path, *cmdline, *build_preload_file;
+ int c, option_index, i, ram_size, accel_enable;
+ BOOL allow_ctrlc;
+ BlockDeviceModeEnum drive_mode;
+ VirtMachineParams p_s, *p = &p_s;
+
+ ram_size = -1;
+ allow_ctrlc = FALSE;
+ (void)allow_ctrlc;
+ drive_mode = BF_MODE_SNAPSHOT;
+ accel_enable = -1;
+ cmdline = NULL;
+ build_preload_file = NULL;
+ for(;;) {
+ c = getopt_long_only(argc, argv, "hm:", options, &option_index);
+ if (c == -1)
+ break;
+ switch(c) {
+ case 0:
+ switch(option_index) {
+ case 1: /* ctrlc */
+ allow_ctrlc = TRUE;
+ break;
+ case 2: /* rw */
+ drive_mode = BF_MODE_RW;
+ break;
+ case 3: /* ro */
+ drive_mode = BF_MODE_RO;
+ break;
+ case 4: /* append */
+ cmdline = optarg;
+ break;
+ case 5: /* no-accel */
+ accel_enable = FALSE;
+ break;
+ case 6: /* build-preload */
+ build_preload_file = optarg;
+ break;
+ default:
+ fprintf(stderr, "unknown option index: %d\n", option_index);
+ exit(1);
+ }
+ break;
+ case 'h':
+ help();
+ break;
+ case 'm':
+ ram_size = strtoul(optarg, NULL, 0);
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ if (optind >= argc) {
+ help();
+ }
+
+ path = argv[optind++];
+
+ virt_machine_set_defaults(p);
+#ifdef CONFIG_FS_NET
+ fs_wget_init();
+#endif
+ virt_machine_load_config_file(p, path, NULL, NULL);
+#ifdef CONFIG_FS_NET
+ fs_net_event_loop(NULL, NULL);
+#endif
+
+ /* override some config parameters */
+
+ if (ram_size > 0) {
+ p->ram_size = (uint64_t)ram_size << 20;
+ }
+ if (accel_enable != -1)
+ p->accel_enable = accel_enable;
+ if (cmdline) {
+ vm_add_cmdline(p, cmdline);
+ }
+
+ /* open the files & devices */
+ for(i = 0; i < p->drive_count; i++) {
+ BlockDevice *drive;
+ char *fname;
+ fname = get_file_path(p->cfg_filename, p->tab_drive[i].filename);
+#ifdef CONFIG_FS_NET
+ if (is_url(fname)) {
+ net_completed = FALSE;
+ drive = block_device_init_http(fname, 128 * 1024,
+ net_start_cb, NULL);
+ /* wait until the drive is initialized */
+ fs_net_event_loop(net_poll_cb, NULL);
+ } else
+#endif
+ {
+ drive = block_device_init(fname, drive_mode);
+ }
+ free(fname);
+ p->tab_drive[i].block_dev = drive;
+ }
+
+ for(i = 0; i < p->fs_count; i++) {
+ FSDevice *fs;
+ const char *path;
+ path = p->tab_fs[i].filename;
+#ifdef CONFIG_FS_NET
+ if (is_url(path)) {
+ fs = fs_net_init(path, NULL, NULL);
+ if (!fs)
+ exit(1);
+ if (build_preload_file)
+ fs_dump_cache_load(fs, build_preload_file);
+ fs_net_event_loop(NULL, NULL);
+ } else
+#endif
+ {
+#ifdef _WIN32
+ fprintf(stderr, "Filesystem access not supported yet\n");
+ exit(1);
+#else
+ char *fname;
+ fname = get_file_path(p->cfg_filename, path);
+ fs = fs_disk_init(fname);
+ if (!fs) {
+ fprintf(stderr, "%s: must be a directory\n", fname);
+ exit(1);
+ }
+ free(fname);
+#endif
+ }
+ p->tab_fs[i].fs_dev = fs;
+ }
+
+ for(i = 0; i < p->eth_count; i++) {
+#ifdef CONFIG_SLIRP
+ if (!strcmp(p->tab_eth[i].driver, "user")) {
+ p->tab_eth[i].net = slirp_open();
+ if (!p->tab_eth[i].net)
+ exit(1);
+ } else
+#endif
+#ifndef _WIN32
+ if (!strcmp(p->tab_eth[i].driver, "tap")) {
+ p->tab_eth[i].net = tun_open(p->tab_eth[i].ifname);
+ if (!p->tab_eth[i].net)
+ exit(1);
+ } else
+#endif
+ {
+ fprintf(stderr, "Unsupported network driver '%s'\n",
+ p->tab_eth[i].driver);
+ exit(1);
+ }
+ }
+
+#ifdef CONFIG_SDL
+ if (p->display_device) {
+ sdl_init(p->width, p->height);
+ } else
+#endif
+ {
+#ifdef _WIN32
+ fprintf(stderr, "Console not supported yet\n");
+ exit(1);
+#else
+ p->console = console_init(allow_ctrlc);
+#endif
+ }
+ p->rtc_real_time = TRUE;
+
+ s = virt_machine_init(p);
+ if (!s)
+ exit(1);
+
+ virt_machine_free_config(p);
+
+ if (s->net) {
+ s->net->device_set_carrier(s->net, TRUE);
+ }
+
+ for(;;) {
+ virt_machine_run(s);
+ }
+ virt_machine_end(s);
+ return 0;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/vga.c b/jslinux-2019-12-21/tinyemu-2019-12-21/vga.c
new file mode 100644
index 0000000..908a320
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/vga.c
@@ -0,0 +1,804 @@
+/*
+ * Dummy VGA device
+ *
+ * Copyright (c) 2003-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "virtio.h"
+#include "machine.h"
+
+//#define DEBUG_VBE
+
+#define MSR_COLOR_EMULATION 0x01
+#define MSR_PAGE_SELECT 0x20
+
+#define ST01_V_RETRACE 0x08
+#define ST01_DISP_ENABLE 0x01
+
+#define VBE_DISPI_INDEX_ID 0x0
+#define VBE_DISPI_INDEX_XRES 0x1
+#define VBE_DISPI_INDEX_YRES 0x2
+#define VBE_DISPI_INDEX_BPP 0x3
+#define VBE_DISPI_INDEX_ENABLE 0x4
+#define VBE_DISPI_INDEX_BANK 0x5
+#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+#define VBE_DISPI_INDEX_X_OFFSET 0x8
+#define VBE_DISPI_INDEX_Y_OFFSET 0x9
+#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
+#define VBE_DISPI_INDEX_NB 0xb
+
+#define VBE_DISPI_ID0 0xB0C0
+#define VBE_DISPI_ID1 0xB0C1
+#define VBE_DISPI_ID2 0xB0C2
+#define VBE_DISPI_ID3 0xB0C3
+#define VBE_DISPI_ID4 0xB0C4
+#define VBE_DISPI_ID5 0xB0C5
+
+#define VBE_DISPI_DISABLED 0x00
+#define VBE_DISPI_ENABLED 0x01
+#define VBE_DISPI_GETCAPS 0x02
+#define VBE_DISPI_8BIT_DAC 0x20
+#define VBE_DISPI_LFB_ENABLED 0x40
+#define VBE_DISPI_NOCLEARMEM 0x80
+
+#define FB_ALLOC_ALIGN (1 << 20)
+
+#define MAX_TEXT_WIDTH 132
+#define MAX_TEXT_HEIGHT 60
+
+struct VGAState {
+ FBDevice *fb_dev;
+ int fb_page_count;
+ PhysMemoryRange *mem_range;
+ PhysMemoryRange *mem_range2;
+ PCIDevice *pci_dev;
+ PhysMemoryRange *rom_range;
+
+ uint8_t *vga_ram; /* 128K at 0xa0000 */
+
+ uint8_t sr_index;
+ uint8_t sr[8];
+ uint8_t gr_index;
+ uint8_t gr[16];
+ uint8_t ar_index;
+ uint8_t ar[21];
+ int ar_flip_flop;
+ uint8_t cr_index;
+ uint8_t cr[256]; /* CRT registers */
+ uint8_t msr; /* Misc Output Register */
+ uint8_t fcr; /* Feature Control Register */
+ uint8_t st00; /* status 0 */
+ uint8_t st01; /* status 1 */
+ uint8_t dac_state;
+ uint8_t dac_sub_index;
+ uint8_t dac_read_index;
+ uint8_t dac_write_index;
+ uint8_t dac_cache[3]; /* used when writing */
+ uint8_t palette[768];
+
+ /* text mode state */
+ uint32_t last_palette[16];
+ uint16_t last_ch_attr[MAX_TEXT_WIDTH * MAX_TEXT_HEIGHT];
+ uint32_t last_width;
+ uint32_t last_height;
+ uint16_t last_line_offset;
+ uint16_t last_start_addr;
+ uint16_t last_cursor_offset;
+ uint8_t last_cursor_start;
+ uint8_t last_cursor_end;
+
+ /* VBE extension */
+ uint16_t vbe_index;
+ uint16_t vbe_regs[VBE_DISPI_INDEX_NB];
+};
+
+static void vga_draw_glyph8(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol)
+{
+ uint32_t font_data, xorcol;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+ font_ptr++;
+ d += linesize;
+ } while (--h);
+}
+
+static void vga_draw_glyph9(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol,
+ int dup9)
+{
+ uint32_t font_data, xorcol, v;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = v;
+ if (dup9)
+ ((uint32_t *)d)[8] = v;
+ else
+ ((uint32_t *)d)[8] = bgcol;
+ font_ptr++;
+ d += linesize;
+ } while (--h);
+}
+
+static const uint8_t cursor_glyph[32] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static inline int c6_to_8(int v)
+{
+ int b;
+ v &= 0x3f;
+ b = v & 1;
+ return (v << 2) | (b << 1) | b;
+}
+
+static int update_palette16(VGAState *s, uint32_t *palette)
+{
+ int full_update, i;
+ uint32_t v, col;
+
+ full_update = 0;
+ for(i = 0; i < 16; i++) {
+ v = s->ar[i];
+ if (s->ar[0x10] & 0x80)
+ v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf);
+ else
+ v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
+ v = v * 3;
+ col = (c6_to_8(s->palette[v]) << 16) |
+ (c6_to_8(s->palette[v + 1]) << 8) |
+ c6_to_8(s->palette[v + 2]);
+ if (col != palette[i]) {
+ full_update = 1;
+ palette[i] = col;
+ }
+ }
+ return full_update;
+}
+
+/* the text refresh is just for debugging and initial boot message, so
+ it is very incomplete */
+static void vga_text_refresh(VGAState *s,
+ SimpleFBDrawFunc *redraw_func, void *opaque)
+{
+ FBDevice *fb_dev = s->fb_dev;
+ int width, height, cwidth, cheight, cy, cx, x1, y1, width1, height1;
+ int cx_min, cx_max, dup9;
+ uint32_t ch_attr, line_offset, start_addr, ch_addr, ch_addr1, ch, cattr;
+ uint8_t *vga_ram, *font_ptr, *dst;
+ uint32_t fgcol, bgcol, cursor_offset, cursor_start, cursor_end;
+ BOOL full_update;
+
+ full_update = update_palette16(s, s->last_palette);
+
+ vga_ram = s->vga_ram;
+
+ line_offset = s->cr[0x13];
+ line_offset <<= 3;
+ line_offset >>= 1;
+
+ start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
+
+ cheight = (s->cr[9] & 0x1f) + 1;
+ cwidth = 8;
+ if (!(s->sr[1] & 0x01))
+ cwidth++;
+
+ width = (s->cr[0x01] + 1);
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1) / cheight;
+
+ width1 = width * cwidth;
+ height1 = height * cheight;
+ if (fb_dev->width < width1 || fb_dev->height < height1 ||
+ width > MAX_TEXT_WIDTH || height > MAX_TEXT_HEIGHT)
+ return; /* not enough space */
+ if (s->last_line_offset != line_offset ||
+ s->last_start_addr != start_addr ||
+ s->last_width != width ||
+ s->last_height != height) {
+ s->last_line_offset = line_offset;
+ s->last_start_addr = start_addr;
+ s->last_width = width;
+ s->last_height = height;
+ full_update = TRUE;
+ }
+
+ /* update cursor position */
+ cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - start_addr;
+ cursor_start = s->cr[0xa];
+ cursor_end = s->cr[0xb];
+ if (cursor_offset != s->last_cursor_offset ||
+ cursor_start != s->last_cursor_start ||
+ cursor_end != s->last_cursor_end) {
+ /* force refresh of characters with the cursor */
+ if (s->last_cursor_offset < MAX_TEXT_WIDTH * MAX_TEXT_HEIGHT)
+ s->last_ch_attr[s->last_cursor_offset] = -1;
+ if (cursor_offset < MAX_TEXT_WIDTH * MAX_TEXT_HEIGHT)
+ s->last_ch_attr[cursor_offset] = -1;
+ s->last_cursor_offset = cursor_offset;
+ s->last_cursor_start = cursor_start;
+ s->last_cursor_end = cursor_end;
+ }
+
+ ch_addr1 = 0x18000 + (start_addr * 2);
+ cursor_offset = 0x18000 + (start_addr + cursor_offset) * 2;
+
+ x1 = (fb_dev->width - width1) / 2;
+ y1 = (fb_dev->height - height1) / 2;
+#if 0
+ printf("text refresh %dx%d font=%dx%d start_addr=0x%x line_offset=0x%x\n",
+ width, height, cwidth, cheight, start_addr, line_offset);
+#endif
+ for(cy = 0; cy < height; cy++) {
+ ch_addr = ch_addr1;
+ dst = fb_dev->fb_data + (y1 + cy * cheight) * fb_dev->stride + x1 * 4;
+ cx_min = width;
+ cx_max = -1;
+ for(cx = 0; cx < width; cx++) {
+ ch_attr = *(uint16_t *)(vga_ram + (ch_addr & 0x1fffe));
+ if (full_update || ch_attr != s->last_ch_attr[cy * width + cx]) {
+ s->last_ch_attr[cy * width + cx] = ch_attr;
+ cx_min = min_int(cx_min, cx);
+ cx_max = max_int(cx_max, cx);
+ ch = ch_attr & 0xff;
+ cattr = ch_attr >> 8;
+ font_ptr = vga_ram + 32 * ch;
+ bgcol = s->last_palette[cattr >> 4];
+ fgcol = s->last_palette[cattr & 0x0f];
+ if (cwidth == 8) {
+ vga_draw_glyph8(dst, fb_dev->stride, font_ptr, cheight,
+ fgcol, bgcol);
+ } else {
+ dup9 = 0;
+ if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04))
+ dup9 = 1;
+ vga_draw_glyph9(dst, fb_dev->stride, font_ptr, cheight,
+ fgcol, bgcol, dup9);
+ }
+ /* cursor display */
+ if (cursor_offset == ch_addr && !(cursor_start & 0x20)) {
+ int line_start, line_last, h;
+ uint8_t *dst1;
+ line_start = cursor_start & 0x1f;
+ line_last = min_int(cursor_end & 0x1f, cheight - 1);
+
+ if (line_last >= line_start && line_start < cheight) {
+ h = line_last - line_start + 1;
+ dst1 = dst + fb_dev->stride * line_start;
+ if (cwidth == 8) {
+ vga_draw_glyph8(dst1, fb_dev->stride,
+ cursor_glyph,
+ h, fgcol, bgcol);
+ } else {
+ vga_draw_glyph9(dst1, fb_dev->stride,
+ cursor_glyph,
+ h, fgcol, bgcol, 1);
+ }
+ }
+ }
+ }
+ ch_addr += 2;
+ dst += 4 * cwidth;
+ }
+ if (cx_max >= cx_min) {
+ // printf("redraw %d %d %d\n", cy, cx_min, cx_max);
+ redraw_func(fb_dev, opaque,
+ x1 + cx_min * cwidth, y1 + cy * cheight,
+ (cx_max - cx_min + 1) * cwidth, cheight);
+ }
+ ch_addr1 += line_offset;
+ }
+}
+
+static void vga_refresh(FBDevice *fb_dev,
+ SimpleFBDrawFunc *redraw_func, void *opaque)
+{
+ VGAState *s = fb_dev->device_opaque;
+
+ if (!(s->ar_index & 0x20)) {
+ /* blank */
+ } else if (s->gr[0x06] & 1) {
+ /* graphic mode (VBE) */
+ simplefb_refresh(fb_dev, redraw_func, opaque, s->mem_range, s->fb_page_count);
+ } else {
+ /* text mode */
+ vga_text_refresh(s, redraw_func, opaque);
+ }
+}
+
+/* force some bits to zero */
+static const uint8_t sr_mask[8] = {
+ (uint8_t)~0xfc,
+ (uint8_t)~0xc2,
+ (uint8_t)~0xf0,
+ (uint8_t)~0xc0,
+ (uint8_t)~0xf1,
+ (uint8_t)~0xff,
+ (uint8_t)~0xff,
+ (uint8_t)~0x00,
+};
+
+static const uint8_t gr_mask[16] = {
+ (uint8_t)~0xf0, /* 0x00 */
+ (uint8_t)~0xf0, /* 0x01 */
+ (uint8_t)~0xf0, /* 0x02 */
+ (uint8_t)~0xe0, /* 0x03 */
+ (uint8_t)~0xfc, /* 0x04 */
+ (uint8_t)~0x84, /* 0x05 */
+ (uint8_t)~0xf0, /* 0x06 */
+ (uint8_t)~0xf0, /* 0x07 */
+ (uint8_t)~0x00, /* 0x08 */
+ (uint8_t)~0xff, /* 0x09 */
+ (uint8_t)~0xff, /* 0x0a */
+ (uint8_t)~0xff, /* 0x0b */
+ (uint8_t)~0xff, /* 0x0c */
+ (uint8_t)~0xff, /* 0x0d */
+ (uint8_t)~0xff, /* 0x0e */
+ (uint8_t)~0xff, /* 0x0f */
+};
+
+static uint32_t vga_ioport_read(VGAState *s, uint32_t addr)
+{
+ int val, index;
+
+ /* check port range access depending on color/monochrome mode */
+ if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
+ (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) {
+ val = 0xff;
+ } else {
+ switch(addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val = s->ar_index;
+ } else {
+ val = 0;
+ }
+ break;
+ case 0x3c1:
+ index = s->ar_index & 0x1f;
+ if (index < 21)
+ val = s->ar[index];
+ else
+ val = 0;
+ break;
+ case 0x3c2:
+ val = s->st00;
+ break;
+ case 0x3c4:
+ val = s->sr_index;
+ break;
+ case 0x3c5:
+ val = s->sr[s->sr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ break;
+ case 0x3c7:
+ val = s->dac_state;
+ break;
+ case 0x3c8:
+ val = s->dac_write_index;
+ break;
+ case 0x3c9:
+ val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
+ if (++s->dac_sub_index == 3) {
+ s->dac_sub_index = 0;
+ s->dac_read_index++;
+ }
+ break;
+ case 0x3ca:
+ val = s->fcr;
+ break;
+ case 0x3cc:
+ val = s->msr;
+ break;
+ case 0x3ce:
+ val = s->gr_index;
+ break;
+ case 0x3cf:
+ val = s->gr[s->gr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ val = s->cr_index;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+ val = s->cr[s->cr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ break;
+ case 0x3ba:
+ case 0x3da:
+ /* just toggle to fool polling */
+ s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE;
+ val = s->st01;
+ s->ar_flip_flop = 0;
+ break;
+ default:
+ val = 0x00;
+ break;
+ }
+ }
+#if defined(DEBUG_VGA)
+ printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void vga_ioport_write(VGAState *s, uint32_t addr, uint32_t val)
+{
+ int index;
+
+ /* check port range access depending on color/monochrome mode */
+ if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
+ (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION)))
+ return;
+
+#ifdef DEBUG_VGA
+ printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+
+ switch(addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val &= 0x3f;
+ s->ar_index = val;
+ } else {
+ index = s->ar_index & 0x1f;
+ switch(index) {
+ case 0x00 ... 0x0f:
+ s->ar[index] = val & 0x3f;
+ break;
+ case 0x10:
+ s->ar[index] = val & ~0x10;
+ break;
+ case 0x11:
+ s->ar[index] = val;
+ break;
+ case 0x12:
+ s->ar[index] = val & ~0xc0;
+ break;
+ case 0x13:
+ s->ar[index] = val & ~0xf0;
+ break;
+ case 0x14:
+ s->ar[index] = val & ~0xf0;
+ break;
+ default:
+ break;
+ }
+ }
+ s->ar_flip_flop ^= 1;
+ break;
+ case 0x3c2:
+ s->msr = val & ~0x10;
+ break;
+ case 0x3c4:
+ s->sr_index = val & 7;
+ break;
+ case 0x3c5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ s->sr[s->sr_index] = val & sr_mask[s->sr_index];
+ break;
+ case 0x3c7:
+ s->dac_read_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 3;
+ break;
+ case 0x3c8:
+ s->dac_write_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 0;
+ break;
+ case 0x3c9:
+ s->dac_cache[s->dac_sub_index] = val;
+ if (++s->dac_sub_index == 3) {
+ memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
+ s->dac_sub_index = 0;
+ s->dac_write_index++;
+ }
+ break;
+ case 0x3ce:
+ s->gr_index = val & 0x0f;
+ break;
+ case 0x3cf:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ s->gr[s->gr_index] = val & gr_mask[s->gr_index];
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ s->cr_index = val;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ /* handle CR0-7 protection */
+ if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
+ /* can always write bit 4 of CR7 */
+ if (s->cr_index == 7)
+ s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
+ return;
+ }
+ switch(s->cr_index) {
+ case 0x01: /* horizontal display end */
+ case 0x07:
+ case 0x09:
+ case 0x0c:
+ case 0x0d:
+ case 0x12: /* vertical display end */
+ s->cr[s->cr_index] = val;
+ break;
+ default:
+ s->cr[s->cr_index] = val;
+ break;
+ }
+ break;
+ case 0x3ba:
+ case 0x3da:
+ s->fcr = val & 0x10;
+ break;
+ }
+}
+
+#define VGA_IO(base) \
+static uint32_t vga_read_ ## base(void *opaque, uint32_t addr, int size_log2)\
+{\
+ return vga_ioport_read(opaque, base + addr);\
+}\
+static void vga_write_ ## base(void *opaque, uint32_t addr, uint32_t val, int size_log2)\
+{\
+ return vga_ioport_write(opaque, base + addr, val);\
+}
+
+VGA_IO(0x3c0)
+VGA_IO(0x3b4)
+VGA_IO(0x3d4)
+VGA_IO(0x3ba)
+VGA_IO(0x3da)
+
+static void vbe_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ VGAState *s = opaque;
+ FBDevice *fb_dev = s->fb_dev;
+
+ if (offset == 0) {
+ s->vbe_index = val;
+ } else {
+#ifdef DEBUG_VBE
+ printf("VBE write: index=0x%04x val=0x%04x\n", s->vbe_index, val);
+#endif
+ switch(s->vbe_index) {
+ case VBE_DISPI_INDEX_ID:
+ if (val >= VBE_DISPI_ID0 && val <= VBE_DISPI_ID5)
+ s->vbe_regs[s->vbe_index] = val;
+ break;
+ case VBE_DISPI_INDEX_ENABLE:
+ if ((val & VBE_DISPI_ENABLED) &&
+ !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
+ /* set graphic mode */
+ /* XXX: resolution change not really supported */
+ if (s->vbe_regs[VBE_DISPI_INDEX_XRES] <= 4096 &&
+ s->vbe_regs[VBE_DISPI_INDEX_YRES] <= 4096 &&
+ (s->vbe_regs[VBE_DISPI_INDEX_XRES] * 4 *
+ s->vbe_regs[VBE_DISPI_INDEX_YRES]) <= fb_dev->fb_size) {
+ fb_dev->width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
+ fb_dev->height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
+ fb_dev->stride = fb_dev->width * 4;
+ }
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] =
+ s->vbe_regs[VBE_DISPI_INDEX_XRES];
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] =
+ s->vbe_regs[VBE_DISPI_INDEX_YRES];
+ s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
+ s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
+ }
+ s->vbe_regs[s->vbe_index] = val;
+ break;
+ case VBE_DISPI_INDEX_XRES:
+ case VBE_DISPI_INDEX_YRES:
+ case VBE_DISPI_INDEX_BPP:
+ case VBE_DISPI_INDEX_BANK:
+ case VBE_DISPI_INDEX_VIRT_WIDTH:
+ case VBE_DISPI_INDEX_VIRT_HEIGHT:
+ case VBE_DISPI_INDEX_X_OFFSET:
+ case VBE_DISPI_INDEX_Y_OFFSET:
+ s->vbe_regs[s->vbe_index] = val;
+ break;
+ }
+ }
+}
+
+static uint32_t vbe_read(void *opaque, uint32_t offset, int size_log2)
+{
+ VGAState *s = opaque;
+ uint32_t val;
+
+ if (offset == 0) {
+ val = s->vbe_index;
+ } else {
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
+ switch(s->vbe_index) {
+ case VBE_DISPI_INDEX_XRES:
+ val = s->fb_dev->width;
+ break;
+ case VBE_DISPI_INDEX_YRES:
+ val = s->fb_dev->height;
+ break;
+ case VBE_DISPI_INDEX_BPP:
+ val = 32;
+ break;
+ default:
+ goto read_reg;
+ }
+ } else {
+ read_reg:
+ if (s->vbe_index < VBE_DISPI_INDEX_NB)
+ val = s->vbe_regs[s->vbe_index];
+ else
+ val = 0;
+ }
+#ifdef DEBUG_VBE
+ printf("VBE read: index=0x%04x val=0x%04x\n", s->vbe_index, val);
+#endif
+ }
+ return val;
+}
+
+
+static void simplefb_bar_set(void *opaque, int bar_num,
+ uint32_t addr, BOOL enabled)
+{
+ VGAState *s = opaque;
+ if (bar_num == 0)
+ phys_mem_set_addr(s->mem_range, addr, enabled);
+ else
+ phys_mem_set_addr(s->rom_range, addr, enabled);
+}
+
+VGAState *pci_vga_init(PCIBus *bus, FBDevice *fb_dev,
+ int width, int height,
+ const uint8_t *vga_rom_buf, int vga_rom_size)
+{
+ VGAState *s;
+ PCIDevice *d;
+ uint32_t bar_size;
+ PhysMemoryMap *mem_map, *port_map;
+
+ d = pci_register_device(bus, "VGA", -1, 0x1234, 0x1111, 0x00, 0x0300);
+
+ mem_map = pci_device_get_mem_map(d);
+ port_map = pci_device_get_port_map(d);
+
+ s = mallocz(sizeof(*s));
+ s->fb_dev = fb_dev;
+
+ fb_dev->width = width;
+ fb_dev->height = height;
+ fb_dev->stride = width * 4;
+
+ fb_dev->fb_size = (height * fb_dev->stride + FB_ALLOC_ALIGN - 1) & ~(FB_ALLOC_ALIGN - 1);
+ s->fb_page_count = fb_dev->fb_size >> DEVRAM_PAGE_SIZE_LOG2;
+
+ s->mem_range =
+ cpu_register_ram(mem_map, 0, fb_dev->fb_size,
+ DEVRAM_FLAG_DIRTY_BITS | DEVRAM_FLAG_DISABLED);
+
+ fb_dev->fb_data = s->mem_range->phys_mem;
+
+ s->pci_dev = d;
+ bar_size = 1;
+ while (bar_size < fb_dev->fb_size)
+ bar_size <<= 1;
+ pci_register_bar(d, 0, bar_size, PCI_ADDRESS_SPACE_MEM, s,
+ simplefb_bar_set);
+
+ if (vga_rom_size > 0) {
+ int rom_size;
+ /* align to page size */
+ rom_size = (vga_rom_size + DEVRAM_PAGE_SIZE - 1) & ~(DEVRAM_PAGE_SIZE - 1);
+ s->rom_range = cpu_register_ram(mem_map, 0, rom_size,
+ DEVRAM_FLAG_ROM | DEVRAM_FLAG_DISABLED);
+ memcpy(s->rom_range->phys_mem, vga_rom_buf, vga_rom_size);
+
+ bar_size = 1;
+ while (bar_size < rom_size)
+ bar_size <<= 1;
+ pci_register_bar(d, PCI_ROM_SLOT, bar_size, PCI_ADDRESS_SPACE_MEM, s,
+ simplefb_bar_set);
+ }
+
+ /* VGA memory (for simple text mode no need for callbacks) */
+ s->mem_range2 = cpu_register_ram(mem_map, 0xa0000, 0x20000, 0);
+ s->vga_ram = s->mem_range2->phys_mem;
+
+ /* standard VGA ports */
+
+ cpu_register_device(port_map, 0x3c0, 16, s, vga_read_0x3c0, vga_write_0x3c0,
+ DEVIO_SIZE8);
+ cpu_register_device(port_map, 0x3b4, 2, s, vga_read_0x3b4, vga_write_0x3b4,
+ DEVIO_SIZE8);
+ cpu_register_device(port_map, 0x3d4, 2, s, vga_read_0x3d4, vga_write_0x3d4,
+ DEVIO_SIZE8);
+ cpu_register_device(port_map, 0x3ba, 1, s, vga_read_0x3ba, vga_write_0x3ba,
+ DEVIO_SIZE8);
+ cpu_register_device(port_map, 0x3da, 1, s, vga_read_0x3da, vga_write_0x3da,
+ DEVIO_SIZE8);
+
+ /* VBE extension */
+ cpu_register_device(port_map, 0x1ce, 2, s, vbe_read, vbe_write,
+ DEVIO_SIZE16);
+
+ s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5;
+ s->vbe_regs[VBE_DISPI_INDEX_VIDEO_MEMORY_64K] = fb_dev->fb_size >> 16;
+
+ fb_dev->device_opaque = s;
+ fb_dev->refresh = vga_refresh;
+ return s;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/virtio.c b/jslinux-2019-12-21/tinyemu-2019-12-21/virtio.c
new file mode 100644
index 0000000..8d07f44
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/virtio.c
@@ -0,0 +1,2650 @@
+/*
+ * VIRTIO driver
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "cutils.h"
+#include "list.h"
+#include "virtio.h"
+
+//#define DEBUG_VIRTIO
+
+/* MMIO addresses - from the Linux kernel */
+#define VIRTIO_MMIO_MAGIC_VALUE 0x000
+#define VIRTIO_MMIO_VERSION 0x004
+#define VIRTIO_MMIO_DEVICE_ID 0x008
+#define VIRTIO_MMIO_VENDOR_ID 0x00c
+#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
+#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014
+#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
+#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024
+#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 /* version 1 only */
+#define VIRTIO_MMIO_QUEUE_SEL 0x030
+#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034
+#define VIRTIO_MMIO_QUEUE_NUM 0x038
+#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c /* version 1 only */
+#define VIRTIO_MMIO_QUEUE_PFN 0x040 /* version 1 only */
+#define VIRTIO_MMIO_QUEUE_READY 0x044
+#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050
+#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060
+#define VIRTIO_MMIO_INTERRUPT_ACK 0x064
+#define VIRTIO_MMIO_STATUS 0x070
+#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080
+#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084
+#define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090
+#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094
+#define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0
+#define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4
+#define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc
+#define VIRTIO_MMIO_CONFIG 0x100
+
+/* PCI registers */
+#define VIRTIO_PCI_DEVICE_FEATURE_SEL 0x000
+#define VIRTIO_PCI_DEVICE_FEATURE 0x004
+#define VIRTIO_PCI_GUEST_FEATURE_SEL 0x008
+#define VIRTIO_PCI_GUEST_FEATURE 0x00c
+#define VIRTIO_PCI_MSIX_CONFIG 0x010
+#define VIRTIO_PCI_NUM_QUEUES 0x012
+#define VIRTIO_PCI_DEVICE_STATUS 0x014
+#define VIRTIO_PCI_CONFIG_GENERATION 0x015
+#define VIRTIO_PCI_QUEUE_SEL 0x016
+#define VIRTIO_PCI_QUEUE_SIZE 0x018
+#define VIRTIO_PCI_QUEUE_MSIX_VECTOR 0x01a
+#define VIRTIO_PCI_QUEUE_ENABLE 0x01c
+#define VIRTIO_PCI_QUEUE_NOTIFY_OFF 0x01e
+#define VIRTIO_PCI_QUEUE_DESC_LOW 0x020
+#define VIRTIO_PCI_QUEUE_DESC_HIGH 0x024
+#define VIRTIO_PCI_QUEUE_AVAIL_LOW 0x028
+#define VIRTIO_PCI_QUEUE_AVAIL_HIGH 0x02c
+#define VIRTIO_PCI_QUEUE_USED_LOW 0x030
+#define VIRTIO_PCI_QUEUE_USED_HIGH 0x034
+
+#define VIRTIO_PCI_CFG_OFFSET 0x0000
+#define VIRTIO_PCI_ISR_OFFSET 0x1000
+#define VIRTIO_PCI_CONFIG_OFFSET 0x2000
+#define VIRTIO_PCI_NOTIFY_OFFSET 0x3000
+
+#define VIRTIO_PCI_CAP_LEN 16
+
+#define MAX_QUEUE 8
+#define MAX_CONFIG_SPACE_SIZE 256
+#define MAX_QUEUE_NUM 16
+
+typedef struct {
+ uint32_t ready; /* 0 or 1 */
+ uint32_t num;
+ uint16_t last_avail_idx;
+ virtio_phys_addr_t desc_addr;
+ virtio_phys_addr_t avail_addr;
+ virtio_phys_addr_t used_addr;
+ BOOL manual_recv; /* if TRUE, the device_recv() callback is not called */
+} QueueState;
+
+#define VRING_DESC_F_NEXT 1
+#define VRING_DESC_F_WRITE 2
+#define VRING_DESC_F_INDIRECT 4
+
+typedef struct {
+ uint64_t addr;
+ uint32_t len;
+ uint16_t flags; /* VRING_DESC_F_x */
+ uint16_t next;
+} VIRTIODesc;
+
+/* return < 0 to stop the notification (it must be manually restarted
+ later), 0 if OK */
+typedef int VIRTIODeviceRecvFunc(VIRTIODevice *s1, int queue_idx,
+ int desc_idx, int read_size,
+ int write_size);
+
+/* return NULL if no RAM at this address. The mapping is valid for one page */
+typedef uint8_t *VIRTIOGetRAMPtrFunc(VIRTIODevice *s, virtio_phys_addr_t paddr, BOOL is_rw);
+
+struct VIRTIODevice {
+ PhysMemoryMap *mem_map;
+ PhysMemoryRange *mem_range;
+ /* PCI only */
+ PCIDevice *pci_dev;
+ /* MMIO only */
+ IRQSignal *irq;
+ VIRTIOGetRAMPtrFunc *get_ram_ptr;
+ int debug;
+
+ uint32_t int_status;
+ uint32_t status;
+ uint32_t device_features_sel;
+ uint32_t queue_sel; /* currently selected queue */
+ QueueState queue[MAX_QUEUE];
+
+ /* device specific */
+ uint32_t device_id;
+ uint32_t vendor_id;
+ uint32_t device_features;
+ VIRTIODeviceRecvFunc *device_recv;
+ void (*config_write)(VIRTIODevice *s); /* called after the config
+ is written */
+ uint32_t config_space_size; /* in bytes, must be multiple of 4 */
+ uint8_t config_space[MAX_CONFIG_SPACE_SIZE];
+};
+
+static uint32_t virtio_mmio_read(void *opaque, uint32_t offset1, int size_log2);
+static void virtio_mmio_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2);
+static uint32_t virtio_pci_read(void *opaque, uint32_t offset, int size_log2);
+static void virtio_pci_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2);
+
+static void virtio_reset(VIRTIODevice *s)
+{
+ int i;
+
+ s->status = 0;
+ s->queue_sel = 0;
+ s->device_features_sel = 0;
+ s->int_status = 0;
+ for(i = 0; i < MAX_QUEUE; i++) {
+ QueueState *qs = &s->queue[i];
+ qs->ready = 0;
+ qs->num = MAX_QUEUE_NUM;
+ qs->desc_addr = 0;
+ qs->avail_addr = 0;
+ qs->used_addr = 0;
+ qs->last_avail_idx = 0;
+ }
+}
+
+static uint8_t *virtio_pci_get_ram_ptr(VIRTIODevice *s, virtio_phys_addr_t paddr, BOOL is_rw)
+{
+ return pci_device_get_dma_ptr(s->pci_dev, paddr, is_rw);
+}
+
+static uint8_t *virtio_mmio_get_ram_ptr(VIRTIODevice *s, virtio_phys_addr_t paddr, BOOL is_rw)
+{
+ return phys_mem_get_ram_ptr(s->mem_map, paddr, is_rw);
+}
+
+static void virtio_add_pci_capability(VIRTIODevice *s, int cfg_type,
+ int bar, uint32_t offset, uint32_t len,
+ uint32_t mult)
+{
+ uint8_t cap[20];
+ int cap_len;
+ if (cfg_type == 2)
+ cap_len = 20;
+ else
+ cap_len = 16;
+ memset(cap, 0, cap_len);
+ cap[0] = 0x09; /* vendor specific */
+ cap[2] = cap_len; /* set by pci_add_capability() */
+ cap[3] = cfg_type;
+ cap[4] = bar;
+ put_le32(cap + 8, offset);
+ put_le32(cap + 12, len);
+ if (cfg_type == 2)
+ put_le32(cap + 16, mult);
+ pci_add_capability(s->pci_dev, cap, cap_len);
+}
+
+static void virtio_pci_bar_set(void *opaque, int bar_num,
+ uint32_t addr, BOOL enabled)
+{
+ VIRTIODevice *s = opaque;
+ phys_mem_set_addr(s->mem_range, addr, enabled);
+}
+
+static void virtio_init(VIRTIODevice *s, VIRTIOBusDef *bus,
+ uint32_t device_id, int config_space_size,
+ VIRTIODeviceRecvFunc *device_recv)
+{
+ memset(s, 0, sizeof(*s));
+
+ if (bus->pci_bus) {
+ uint16_t pci_device_id, class_id;
+ char name[32];
+ int bar_num;
+
+ switch(device_id) {
+ case 1:
+ pci_device_id = 0x1000; /* net */
+ class_id = 0x0200;
+ break;
+ case 2:
+ pci_device_id = 0x1001; /* block */
+ class_id = 0x0100; /* XXX: check it */
+ break;
+ case 3:
+ pci_device_id = 0x1003; /* console */
+ class_id = 0x0780;
+ break;
+ case 9:
+ pci_device_id = 0x1040 + device_id; /* use new device ID */
+ class_id = 0x2;
+ break;
+ case 18:
+ pci_device_id = 0x1040 + device_id; /* use new device ID */
+ class_id = 0x0980;
+ break;
+ default:
+ abort();
+ }
+ snprintf(name, sizeof(name), "virtio_%04x", pci_device_id);
+ s->pci_dev = pci_register_device(bus->pci_bus, name, -1,
+ 0x1af4, pci_device_id, 0x00,
+ class_id);
+ pci_device_set_config16(s->pci_dev, 0x2c, 0x1af4);
+ pci_device_set_config16(s->pci_dev, 0x2e, device_id);
+ pci_device_set_config8(s->pci_dev, PCI_INTERRUPT_PIN, 1);
+
+ bar_num = 4;
+ virtio_add_pci_capability(s, 1, bar_num,
+ VIRTIO_PCI_CFG_OFFSET, 0x1000, 0); /* common */
+ virtio_add_pci_capability(s, 3, bar_num,
+ VIRTIO_PCI_ISR_OFFSET, 0x1000, 0); /* isr */
+ virtio_add_pci_capability(s, 4, bar_num,
+ VIRTIO_PCI_CONFIG_OFFSET, 0x1000, 0); /* config */
+ virtio_add_pci_capability(s, 2, bar_num,
+ VIRTIO_PCI_NOTIFY_OFFSET, 0x1000, 0); /* notify */
+
+ s->get_ram_ptr = virtio_pci_get_ram_ptr;
+ s->irq = pci_device_get_irq(s->pci_dev, 0);
+ s->mem_map = pci_device_get_mem_map(s->pci_dev);
+ s->mem_range = cpu_register_device(s->mem_map, 0, 0x4000, s,
+ virtio_pci_read, virtio_pci_write,
+ DEVIO_SIZE8 | DEVIO_SIZE16 | DEVIO_SIZE32 | DEVIO_DISABLED);
+ pci_register_bar(s->pci_dev, bar_num, 0x4000, PCI_ADDRESS_SPACE_MEM,
+ s, virtio_pci_bar_set);
+ } else {
+ /* MMIO case */
+ s->mem_map = bus->mem_map;
+ s->irq = bus->irq;
+ s->mem_range = cpu_register_device(s->mem_map, bus->addr, VIRTIO_PAGE_SIZE,
+ s, virtio_mmio_read, virtio_mmio_write,
+ DEVIO_SIZE8 | DEVIO_SIZE16 | DEVIO_SIZE32);
+ s->get_ram_ptr = virtio_mmio_get_ram_ptr;
+ }
+
+ s->device_id = device_id;
+ s->vendor_id = 0xffff;
+ s->config_space_size = config_space_size;
+ s->device_recv = device_recv;
+ virtio_reset(s);
+}
+
+static uint16_t virtio_read16(VIRTIODevice *s, virtio_phys_addr_t addr)
+{
+ uint8_t *ptr;
+ if (addr & 1)
+ return 0; /* unaligned access are not supported */
+ ptr = s->get_ram_ptr(s, addr, FALSE);
+ if (!ptr)
+ return 0;
+ return *(uint16_t *)ptr;
+}
+
+static void virtio_write16(VIRTIODevice *s, virtio_phys_addr_t addr,
+ uint16_t val)
+{
+ uint8_t *ptr;
+ if (addr & 1)
+ return; /* unaligned access are not supported */
+ ptr = s->get_ram_ptr(s, addr, TRUE);
+ if (!ptr)
+ return;
+ *(uint16_t *)ptr = val;
+}
+
+static void virtio_write32(VIRTIODevice *s, virtio_phys_addr_t addr,
+ uint32_t val)
+{
+ uint8_t *ptr;
+ if (addr & 3)
+ return; /* unaligned access are not supported */
+ ptr = s->get_ram_ptr(s, addr, TRUE);
+ if (!ptr)
+ return;
+ *(uint32_t *)ptr = val;
+}
+
+static int virtio_memcpy_from_ram(VIRTIODevice *s, uint8_t *buf,
+ virtio_phys_addr_t addr, int count)
+{
+ uint8_t *ptr;
+ int l;
+
+ while (count > 0) {
+ l = min_int(count, VIRTIO_PAGE_SIZE - (addr & (VIRTIO_PAGE_SIZE - 1)));
+ ptr = s->get_ram_ptr(s, addr, FALSE);
+ if (!ptr)
+ return -1;
+ memcpy(buf, ptr, l);
+ addr += l;
+ buf += l;
+ count -= l;
+ }
+ return 0;
+}
+
+static int virtio_memcpy_to_ram(VIRTIODevice *s, virtio_phys_addr_t addr,
+ const uint8_t *buf, int count)
+{
+ uint8_t *ptr;
+ int l;
+
+ while (count > 0) {
+ l = min_int(count, VIRTIO_PAGE_SIZE - (addr & (VIRTIO_PAGE_SIZE - 1)));
+ ptr = s->get_ram_ptr(s, addr, TRUE);
+ if (!ptr)
+ return -1;
+ memcpy(ptr, buf, l);
+ addr += l;
+ buf += l;
+ count -= l;
+ }
+ return 0;
+}
+
+static int get_desc(VIRTIODevice *s, VIRTIODesc *desc,
+ int queue_idx, int desc_idx)
+{
+ QueueState *qs = &s->queue[queue_idx];
+ return virtio_memcpy_from_ram(s, (void *)desc, qs->desc_addr +
+ desc_idx * sizeof(VIRTIODesc),
+ sizeof(VIRTIODesc));
+}
+
+static int memcpy_to_from_queue(VIRTIODevice *s, uint8_t *buf,
+ int queue_idx, int desc_idx,
+ int offset, int count, BOOL to_queue)
+{
+ VIRTIODesc desc;
+ int l, f_write_flag;
+
+ if (count == 0)
+ return 0;
+
+ get_desc(s, &desc, queue_idx, desc_idx);
+
+ if (to_queue) {
+ f_write_flag = VRING_DESC_F_WRITE;
+ /* find the first write descriptor */
+ for(;;) {
+ if ((desc.flags & VRING_DESC_F_WRITE) == f_write_flag)
+ break;
+ if (!(desc.flags & VRING_DESC_F_NEXT))
+ return -1;
+ desc_idx = desc.next;
+ get_desc(s, &desc, queue_idx, desc_idx);
+ }
+ } else {
+ f_write_flag = 0;
+ }
+
+ /* find the descriptor at offset */
+ for(;;) {
+ if ((desc.flags & VRING_DESC_F_WRITE) != f_write_flag)
+ return -1;
+ if (offset < desc.len)
+ break;
+ if (!(desc.flags & VRING_DESC_F_NEXT))
+ return -1;
+ desc_idx = desc.next;
+ offset -= desc.len;
+ get_desc(s, &desc, queue_idx, desc_idx);
+ }
+
+ for(;;) {
+ l = min_int(count, desc.len - offset);
+ if (to_queue)
+ virtio_memcpy_to_ram(s, desc.addr + offset, buf, l);
+ else
+ virtio_memcpy_from_ram(s, buf, desc.addr + offset, l);
+ count -= l;
+ if (count == 0)
+ break;
+ offset += l;
+ buf += l;
+ if (offset == desc.len) {
+ if (!(desc.flags & VRING_DESC_F_NEXT))
+ return -1;
+ desc_idx = desc.next;
+ get_desc(s, &desc, queue_idx, desc_idx);
+ if ((desc.flags & VRING_DESC_F_WRITE) != f_write_flag)
+ return -1;
+ offset = 0;
+ }
+ }
+ return 0;
+}
+
+static int memcpy_from_queue(VIRTIODevice *s, void *buf,
+ int queue_idx, int desc_idx,
+ int offset, int count)
+{
+ return memcpy_to_from_queue(s, buf, queue_idx, desc_idx, offset, count,
+ FALSE);
+}
+
+static int memcpy_to_queue(VIRTIODevice *s,
+ int queue_idx, int desc_idx,
+ int offset, const void *buf, int count)
+{
+ return memcpy_to_from_queue(s, (void *)buf, queue_idx, desc_idx, offset,
+ count, TRUE);
+}
+
+/* signal that the descriptor has been consumed */
+static void virtio_consume_desc(VIRTIODevice *s,
+ int queue_idx, int desc_idx, int desc_len)
+{
+ QueueState *qs = &s->queue[queue_idx];
+ virtio_phys_addr_t addr;
+ uint32_t index;
+
+ addr = qs->used_addr + 2;
+ index = virtio_read16(s, addr);
+ virtio_write16(s, addr, index + 1);
+
+ addr = qs->used_addr + 4 + (index & (qs->num - 1)) * 8;
+ virtio_write32(s, addr, desc_idx);
+ virtio_write32(s, addr + 4, desc_len);
+
+ s->int_status |= 1;
+ set_irq(s->irq, 1);
+}
+
+static int get_desc_rw_size(VIRTIODevice *s,
+ int *pread_size, int *pwrite_size,
+ int queue_idx, int desc_idx)
+{
+ VIRTIODesc desc;
+ int read_size, write_size;
+
+ read_size = 0;
+ write_size = 0;
+ get_desc(s, &desc, queue_idx, desc_idx);
+
+ for(;;) {
+ if (desc.flags & VRING_DESC_F_WRITE)
+ break;
+ read_size += desc.len;
+ if (!(desc.flags & VRING_DESC_F_NEXT))
+ goto done;
+ desc_idx = desc.next;
+ get_desc(s, &desc, queue_idx, desc_idx);
+ }
+
+ for(;;) {
+ if (!(desc.flags & VRING_DESC_F_WRITE))
+ return -1;
+ write_size += desc.len;
+ if (!(desc.flags & VRING_DESC_F_NEXT))
+ break;
+ desc_idx = desc.next;
+ get_desc(s, &desc, queue_idx, desc_idx);
+ }
+
+ done:
+ *pread_size = read_size;
+ *pwrite_size = write_size;
+ return 0;
+}
+
+/* XXX: test if the queue is ready ? */
+static void queue_notify(VIRTIODevice *s, int queue_idx)
+{
+ QueueState *qs = &s->queue[queue_idx];
+ uint16_t avail_idx;
+ int desc_idx, read_size, write_size;
+
+ if (qs->manual_recv)
+ return;
+
+ avail_idx = virtio_read16(s, qs->avail_addr + 2);
+ while (qs->last_avail_idx != avail_idx) {
+ desc_idx = virtio_read16(s, qs->avail_addr + 4 +
+ (qs->last_avail_idx & (qs->num - 1)) * 2);
+ if (!get_desc_rw_size(s, &read_size, &write_size, queue_idx, desc_idx)) {
+#ifdef DEBUG_VIRTIO
+ if (s->debug & VIRTIO_DEBUG_IO) {
+ printf("queue_notify: idx=%d read_size=%d write_size=%d\n",
+ queue_idx, read_size, write_size);
+ }
+#endif
+ if (s->device_recv(s, queue_idx, desc_idx,
+ read_size, write_size) < 0)
+ break;
+ }
+ qs->last_avail_idx++;
+ }
+}
+
+static uint32_t virtio_config_read(VIRTIODevice *s, uint32_t offset,
+ int size_log2)
+{
+ uint32_t val;
+ switch(size_log2) {
+ case 0:
+ if (offset < s->config_space_size) {
+ val = s->config_space[offset];
+ } else {
+ val = 0;
+ }
+ break;
+ case 1:
+ if (offset < (s->config_space_size - 1)) {
+ val = get_le16(&s->config_space[offset]);
+ } else {
+ val = 0;
+ }
+ break;
+ case 2:
+ if (offset < (s->config_space_size - 3)) {
+ val = get_le32(s->config_space + offset);
+ } else {
+ val = 0;
+ }
+ break;
+ default:
+ abort();
+ }
+ return val;
+}
+
+static void virtio_config_write(VIRTIODevice *s, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ switch(size_log2) {
+ case 0:
+ if (offset < s->config_space_size) {
+ s->config_space[offset] = val;
+ if (s->config_write)
+ s->config_write(s);
+ }
+ break;
+ case 1:
+ if (offset < s->config_space_size - 1) {
+ put_le16(s->config_space + offset, val);
+ if (s->config_write)
+ s->config_write(s);
+ }
+ break;
+ case 2:
+ if (offset < s->config_space_size - 3) {
+ put_le32(s->config_space + offset, val);
+ if (s->config_write)
+ s->config_write(s);
+ }
+ break;
+ }
+}
+
+static uint32_t virtio_mmio_read(void *opaque, uint32_t offset, int size_log2)
+{
+ VIRTIODevice *s = opaque;
+ uint32_t val;
+
+ if (offset >= VIRTIO_MMIO_CONFIG) {
+ return virtio_config_read(s, offset - VIRTIO_MMIO_CONFIG, size_log2);
+ }
+
+ if (size_log2 == 2) {
+ switch(offset) {
+ case VIRTIO_MMIO_MAGIC_VALUE:
+ val = 0x74726976;
+ break;
+ case VIRTIO_MMIO_VERSION:
+ val = 2;
+ break;
+ case VIRTIO_MMIO_DEVICE_ID:
+ val = s->device_id;
+ break;
+ case VIRTIO_MMIO_VENDOR_ID:
+ val = s->vendor_id;
+ break;
+ case VIRTIO_MMIO_DEVICE_FEATURES:
+ switch(s->device_features_sel) {
+ case 0:
+ val = s->device_features;
+ break;
+ case 1:
+ val = 1; /* version 1 */
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ break;
+ case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
+ val = s->device_features_sel;
+ break;
+ case VIRTIO_MMIO_QUEUE_SEL:
+ val = s->queue_sel;
+ break;
+ case VIRTIO_MMIO_QUEUE_NUM_MAX:
+ val = MAX_QUEUE_NUM;
+ break;
+ case VIRTIO_MMIO_QUEUE_NUM:
+ val = s->queue[s->queue_sel].num;
+ break;
+ case VIRTIO_MMIO_QUEUE_DESC_LOW:
+ val = s->queue[s->queue_sel].desc_addr;
+ break;
+ case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
+ val = s->queue[s->queue_sel].avail_addr;
+ break;
+ case VIRTIO_MMIO_QUEUE_USED_LOW:
+ val = s->queue[s->queue_sel].used_addr;
+ break;
+#if VIRTIO_ADDR_BITS == 64
+ case VIRTIO_MMIO_QUEUE_DESC_HIGH:
+ val = s->queue[s->queue_sel].desc_addr >> 32;
+ break;
+ case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
+ val = s->queue[s->queue_sel].avail_addr >> 32;
+ break;
+ case VIRTIO_MMIO_QUEUE_USED_HIGH:
+ val = s->queue[s->queue_sel].used_addr >> 32;
+ break;
+#endif
+ case VIRTIO_MMIO_QUEUE_READY:
+ val = s->queue[s->queue_sel].ready;
+ break;
+ case VIRTIO_MMIO_INTERRUPT_STATUS:
+ val = s->int_status;
+ break;
+ case VIRTIO_MMIO_STATUS:
+ val = s->status;
+ break;
+ case VIRTIO_MMIO_CONFIG_GENERATION:
+ val = 0;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ } else {
+ val = 0;
+ }
+#ifdef DEBUG_VIRTIO
+ if (s->debug & VIRTIO_DEBUG_IO) {
+ printf("virto_mmio_read: offset=0x%x val=0x%x size=%d\n",
+ offset, val, 1 << size_log2);
+ }
+#endif
+ return val;
+}
+
+#if VIRTIO_ADDR_BITS == 64
+static void set_low32(virtio_phys_addr_t *paddr, uint32_t val)
+{
+ *paddr = (*paddr & ~(virtio_phys_addr_t)0xffffffff) | val;
+}
+
+static void set_high32(virtio_phys_addr_t *paddr, uint32_t val)
+{
+ *paddr = (*paddr & 0xffffffff) | ((virtio_phys_addr_t)val << 32);
+}
+#else
+static void set_low32(virtio_phys_addr_t *paddr, uint32_t val)
+{
+ *paddr = val;
+}
+#endif
+
+static void virtio_mmio_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ VIRTIODevice *s = opaque;
+
+#ifdef DEBUG_VIRTIO
+ if (s->debug & VIRTIO_DEBUG_IO) {
+ printf("virto_mmio_write: offset=0x%x val=0x%x size=%d\n",
+ offset, val, 1 << size_log2);
+ }
+#endif
+
+ if (offset >= VIRTIO_MMIO_CONFIG) {
+ virtio_config_write(s, offset - VIRTIO_MMIO_CONFIG, val, size_log2);
+ return;
+ }
+
+ if (size_log2 == 2) {
+ switch(offset) {
+ case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
+ s->device_features_sel = val;
+ break;
+ case VIRTIO_MMIO_QUEUE_SEL:
+ if (val < MAX_QUEUE)
+ s->queue_sel = val;
+ break;
+ case VIRTIO_MMIO_QUEUE_NUM:
+ if ((val & (val - 1)) == 0 && val > 0) {
+ s->queue[s->queue_sel].num = val;
+ }
+ break;
+ case VIRTIO_MMIO_QUEUE_DESC_LOW:
+ set_low32(&s->queue[s->queue_sel].desc_addr, val);
+ break;
+ case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
+ set_low32(&s->queue[s->queue_sel].avail_addr, val);
+ break;
+ case VIRTIO_MMIO_QUEUE_USED_LOW:
+ set_low32(&s->queue[s->queue_sel].used_addr, val);
+ break;
+#if VIRTIO_ADDR_BITS == 64
+ case VIRTIO_MMIO_QUEUE_DESC_HIGH:
+ set_high32(&s->queue[s->queue_sel].desc_addr, val);
+ break;
+ case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
+ set_high32(&s->queue[s->queue_sel].avail_addr, val);
+ break;
+ case VIRTIO_MMIO_QUEUE_USED_HIGH:
+ set_high32(&s->queue[s->queue_sel].used_addr, val);
+ break;
+#endif
+ case VIRTIO_MMIO_STATUS:
+ s->status = val;
+ if (val == 0) {
+ /* reset */
+ set_irq(s->irq, 0);
+ virtio_reset(s);
+ }
+ break;
+ case VIRTIO_MMIO_QUEUE_READY:
+ s->queue[s->queue_sel].ready = val & 1;
+ break;
+ case VIRTIO_MMIO_QUEUE_NOTIFY:
+ if (val < MAX_QUEUE)
+ queue_notify(s, val);
+ break;
+ case VIRTIO_MMIO_INTERRUPT_ACK:
+ s->int_status &= ~val;
+ if (s->int_status == 0) {
+ set_irq(s->irq, 0);
+ }
+ break;
+ }
+ }
+}
+
+static uint32_t virtio_pci_read(void *opaque, uint32_t offset1, int size_log2)
+{
+ VIRTIODevice *s = opaque;
+ uint32_t offset;
+ uint32_t val = 0;
+
+ offset = offset1 & 0xfff;
+ switch(offset1 >> 12) {
+ case VIRTIO_PCI_CFG_OFFSET >> 12:
+ if (size_log2 == 2) {
+ switch(offset) {
+ case VIRTIO_PCI_DEVICE_FEATURE:
+ switch(s->device_features_sel) {
+ case 0:
+ val = s->device_features;
+ break;
+ case 1:
+ val = 1; /* version 1 */
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ break;
+ case VIRTIO_PCI_DEVICE_FEATURE_SEL:
+ val = s->device_features_sel;
+ break;
+ case VIRTIO_PCI_QUEUE_DESC_LOW:
+ val = s->queue[s->queue_sel].desc_addr;
+ break;
+ case VIRTIO_PCI_QUEUE_AVAIL_LOW:
+ val = s->queue[s->queue_sel].avail_addr;
+ break;
+ case VIRTIO_PCI_QUEUE_USED_LOW:
+ val = s->queue[s->queue_sel].used_addr;
+ break;
+#if VIRTIO_ADDR_BITS == 64
+ case VIRTIO_PCI_QUEUE_DESC_HIGH:
+ val = s->queue[s->queue_sel].desc_addr >> 32;
+ break;
+ case VIRTIO_PCI_QUEUE_AVAIL_HIGH:
+ val = s->queue[s->queue_sel].avail_addr >> 32;
+ break;
+ case VIRTIO_PCI_QUEUE_USED_HIGH:
+ val = s->queue[s->queue_sel].used_addr >> 32;
+ break;
+#endif
+ }
+ } else if (size_log2 == 1) {
+ switch(offset) {
+ case VIRTIO_PCI_NUM_QUEUES:
+ val = MAX_QUEUE_NUM;
+ break;
+ case VIRTIO_PCI_QUEUE_SEL:
+ val = s->queue_sel;
+ break;
+ case VIRTIO_PCI_QUEUE_SIZE:
+ val = s->queue[s->queue_sel].num;
+ break;
+ case VIRTIO_PCI_QUEUE_ENABLE:
+ val = s->queue[s->queue_sel].ready;
+ break;
+ case VIRTIO_PCI_QUEUE_NOTIFY_OFF:
+ val = 0;
+ break;
+ }
+ } else if (size_log2 == 0) {
+ switch(offset) {
+ case VIRTIO_PCI_DEVICE_STATUS:
+ val = s->status;
+ break;
+ }
+ }
+ break;
+ case VIRTIO_PCI_ISR_OFFSET >> 12:
+ if (offset == 0 && size_log2 == 0) {
+ val = s->int_status;
+ s->int_status = 0;
+ set_irq(s->irq, 0);
+ }
+ break;
+ case VIRTIO_PCI_CONFIG_OFFSET >> 12:
+ val = virtio_config_read(s, offset, size_log2);
+ break;
+ }
+#ifdef DEBUG_VIRTIO
+ if (s->debug & VIRTIO_DEBUG_IO) {
+ printf("virto_pci_read: offset=0x%x val=0x%x size=%d\n",
+ offset1, val, 1 << size_log2);
+ }
+#endif
+ return val;
+}
+
+static void virtio_pci_write(void *opaque, uint32_t offset1,
+ uint32_t val, int size_log2)
+{
+ VIRTIODevice *s = opaque;
+ uint32_t offset;
+
+#ifdef DEBUG_VIRTIO
+ if (s->debug & VIRTIO_DEBUG_IO) {
+ printf("virto_pci_write: offset=0x%x val=0x%x size=%d\n",
+ offset1, val, 1 << size_log2);
+ }
+#endif
+ offset = offset1 & 0xfff;
+ switch(offset1 >> 12) {
+ case VIRTIO_PCI_CFG_OFFSET >> 12:
+ if (size_log2 == 2) {
+ switch(offset) {
+ case VIRTIO_PCI_DEVICE_FEATURE_SEL:
+ s->device_features_sel = val;
+ break;
+ case VIRTIO_PCI_QUEUE_DESC_LOW:
+ set_low32(&s->queue[s->queue_sel].desc_addr, val);
+ break;
+ case VIRTIO_PCI_QUEUE_AVAIL_LOW:
+ set_low32(&s->queue[s->queue_sel].avail_addr, val);
+ break;
+ case VIRTIO_PCI_QUEUE_USED_LOW:
+ set_low32(&s->queue[s->queue_sel].used_addr, val);
+ break;
+#if VIRTIO_ADDR_BITS == 64
+ case VIRTIO_PCI_QUEUE_DESC_HIGH:
+ set_high32(&s->queue[s->queue_sel].desc_addr, val);
+ break;
+ case VIRTIO_PCI_QUEUE_AVAIL_HIGH:
+ set_high32(&s->queue[s->queue_sel].avail_addr, val);
+ break;
+ case VIRTIO_PCI_QUEUE_USED_HIGH:
+ set_high32(&s->queue[s->queue_sel].used_addr, val);
+ break;
+#endif
+ }
+ } else if (size_log2 == 1) {
+ switch(offset) {
+ case VIRTIO_PCI_QUEUE_SEL:
+ if (val < MAX_QUEUE)
+ s->queue_sel = val;
+ break;
+ case VIRTIO_PCI_QUEUE_SIZE:
+ if ((val & (val - 1)) == 0 && val > 0) {
+ s->queue[s->queue_sel].num = val;
+ }
+ break;
+ case VIRTIO_PCI_QUEUE_ENABLE:
+ s->queue[s->queue_sel].ready = val & 1;
+ break;
+ }
+ } else if (size_log2 == 0) {
+ switch(offset) {
+ case VIRTIO_PCI_DEVICE_STATUS:
+ s->status = val;
+ if (val == 0) {
+ /* reset */
+ set_irq(s->irq, 0);
+ virtio_reset(s);
+ }
+ break;
+ }
+ }
+ break;
+ case VIRTIO_PCI_CONFIG_OFFSET >> 12:
+ virtio_config_write(s, offset, val, size_log2);
+ break;
+ case VIRTIO_PCI_NOTIFY_OFFSET >> 12:
+ if (val < MAX_QUEUE)
+ queue_notify(s, val);
+ break;
+ }
+}
+
+void virtio_set_debug(VIRTIODevice *s, int debug)
+{
+ s->debug = debug;
+}
+
+static void virtio_config_change_notify(VIRTIODevice *s)
+{
+ /* INT_CONFIG interrupt */
+ s->int_status |= 2;
+ set_irq(s->irq, 1);
+}
+
+/*********************************************************************/
+/* block device */
+
+typedef struct {
+ uint32_t type;
+ uint8_t *buf;
+ int write_size;
+ int queue_idx;
+ int desc_idx;
+} BlockRequest;
+
+typedef struct VIRTIOBlockDevice {
+ VIRTIODevice common;
+ BlockDevice *bs;
+
+ BOOL req_in_progress;
+ BlockRequest req; /* request in progress */
+} VIRTIOBlockDevice;
+
+typedef struct {
+ uint32_t type;
+ uint32_t ioprio;
+ uint64_t sector_num;
+} BlockRequestHeader;
+
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+#define VIRTIO_BLK_T_FLUSH 4
+#define VIRTIO_BLK_T_FLUSH_OUT 5
+
+#define VIRTIO_BLK_S_OK 0
+#define VIRTIO_BLK_S_IOERR 1
+#define VIRTIO_BLK_S_UNSUPP 2
+
+#define SECTOR_SIZE 512
+
+static void virtio_block_req_end(VIRTIODevice *s, int ret)
+{
+ VIRTIOBlockDevice *s1 = (VIRTIOBlockDevice *)s;
+ int write_size;
+ int queue_idx = s1->req.queue_idx;
+ int desc_idx = s1->req.desc_idx;
+ uint8_t *buf, buf1[1];
+
+ switch(s1->req.type) {
+ case VIRTIO_BLK_T_IN:
+ write_size = s1->req.write_size;
+ buf = s1->req.buf;
+ if (ret < 0) {
+ buf[write_size - 1] = VIRTIO_BLK_S_IOERR;
+ } else {
+ buf[write_size - 1] = VIRTIO_BLK_S_OK;
+ }
+ memcpy_to_queue(s, queue_idx, desc_idx, 0, buf, write_size);
+ free(buf);
+ virtio_consume_desc(s, queue_idx, desc_idx, write_size);
+ break;
+ case VIRTIO_BLK_T_OUT:
+ if (ret < 0)
+ buf1[0] = VIRTIO_BLK_S_IOERR;
+ else
+ buf1[0] = VIRTIO_BLK_S_OK;
+ memcpy_to_queue(s, queue_idx, desc_idx, 0, buf1, sizeof(buf1));
+ virtio_consume_desc(s, queue_idx, desc_idx, 1);
+ break;
+ default:
+ abort();
+ }
+}
+
+static void virtio_block_req_cb(void *opaque, int ret)
+{
+ VIRTIODevice *s = opaque;
+ VIRTIOBlockDevice *s1 = (VIRTIOBlockDevice *)s;
+
+ virtio_block_req_end(s, ret);
+
+ s1->req_in_progress = FALSE;
+
+ /* handle next requests */
+ queue_notify((VIRTIODevice *)s, s1->req.queue_idx);
+}
+
+/* XXX: handle async I/O */
+static int virtio_block_recv_request(VIRTIODevice *s, int queue_idx,
+ int desc_idx, int read_size,
+ int write_size)
+{
+ VIRTIOBlockDevice *s1 = (VIRTIOBlockDevice *)s;
+ BlockDevice *bs = s1->bs;
+ BlockRequestHeader h;
+ uint8_t *buf;
+ int len, ret;
+
+ if (s1->req_in_progress)
+ return -1;
+
+ if (memcpy_from_queue(s, &h, queue_idx, desc_idx, 0, sizeof(h)) < 0)
+ return 0;
+ s1->req.type = h.type;
+ s1->req.queue_idx = queue_idx;
+ s1->req.desc_idx = desc_idx;
+ switch(h.type) {
+ case VIRTIO_BLK_T_IN:
+ s1->req.buf = malloc(write_size);
+ s1->req.write_size = write_size;
+ ret = bs->read_async(bs, h.sector_num, s1->req.buf,
+ (write_size - 1) / SECTOR_SIZE,
+ virtio_block_req_cb, s);
+ if (ret > 0) {
+ /* asyncronous read */
+ s1->req_in_progress = TRUE;
+ } else {
+ virtio_block_req_end(s, ret);
+ }
+ break;
+ case VIRTIO_BLK_T_OUT:
+ assert(write_size >= 1);
+ len = read_size - sizeof(h);
+ buf = malloc(len);
+ memcpy_from_queue(s, buf, queue_idx, desc_idx, sizeof(h), len);
+ ret = bs->write_async(bs, h.sector_num, buf, len / SECTOR_SIZE,
+ virtio_block_req_cb, s);
+ free(buf);
+ if (ret > 0) {
+ /* asyncronous write */
+ s1->req_in_progress = TRUE;
+ } else {
+ virtio_block_req_end(s, ret);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+VIRTIODevice *virtio_block_init(VIRTIOBusDef *bus, BlockDevice *bs)
+{
+ VIRTIOBlockDevice *s;
+ uint64_t nb_sectors;
+
+ s = mallocz(sizeof(*s));
+ virtio_init(&s->common, bus,
+ 2, 8, virtio_block_recv_request);
+ s->bs = bs;
+
+ nb_sectors = bs->get_sector_count(bs);
+ put_le32(s->common.config_space, nb_sectors);
+ put_le32(s->common.config_space + 4, nb_sectors >> 32);
+
+ return (VIRTIODevice *)s;
+}
+
+/*********************************************************************/
+/* network device */
+
+typedef struct VIRTIONetDevice {
+ VIRTIODevice common;
+ EthernetDevice *es;
+ int header_size;
+} VIRTIONetDevice;
+
+typedef struct {
+ uint8_t flags;
+ uint8_t gso_type;
+ uint16_t hdr_len;
+ uint16_t gso_size;
+ uint16_t csum_start;
+ uint16_t csum_offset;
+ uint16_t num_buffers;
+} VIRTIONetHeader;
+
+static int virtio_net_recv_request(VIRTIODevice *s, int queue_idx,
+ int desc_idx, int read_size,
+ int write_size)
+{
+ VIRTIONetDevice *s1 = (VIRTIONetDevice *)s;
+ EthernetDevice *es = s1->es;
+ VIRTIONetHeader h;
+ uint8_t *buf;
+ int len;
+
+ if (queue_idx == 1) {
+ /* send to network */
+ if (memcpy_from_queue(s, &h, queue_idx, desc_idx, 0, s1->header_size) < 0)
+ return 0;
+ len = read_size - s1->header_size;
+ buf = malloc(len);
+ memcpy_from_queue(s, buf, queue_idx, desc_idx, s1->header_size, len);
+ es->write_packet(es, buf, len);
+ free(buf);
+ virtio_consume_desc(s, queue_idx, desc_idx, 0);
+ }
+ return 0;
+}
+
+static BOOL virtio_net_can_write_packet(EthernetDevice *es)
+{
+ VIRTIODevice *s = es->device_opaque;
+ QueueState *qs = &s->queue[0];
+ uint16_t avail_idx;
+
+ if (!qs->ready)
+ return FALSE;
+ avail_idx = virtio_read16(s, qs->avail_addr + 2);
+ return qs->last_avail_idx != avail_idx;
+}
+
+static void virtio_net_write_packet(EthernetDevice *es, const uint8_t *buf, int buf_len)
+{
+ VIRTIODevice *s = es->device_opaque;
+ VIRTIONetDevice *s1 = (VIRTIONetDevice *)s;
+ int queue_idx = 0;
+ QueueState *qs = &s->queue[queue_idx];
+ int desc_idx;
+ VIRTIONetHeader h;
+ int len, read_size, write_size;
+ uint16_t avail_idx;
+
+ if (!qs->ready)
+ return;
+ avail_idx = virtio_read16(s, qs->avail_addr + 2);
+ if (qs->last_avail_idx == avail_idx)
+ return;
+ desc_idx = virtio_read16(s, qs->avail_addr + 4 +
+ (qs->last_avail_idx & (qs->num - 1)) * 2);
+ if (get_desc_rw_size(s, &read_size, &write_size, queue_idx, desc_idx))
+ return;
+ len = s1->header_size + buf_len;
+ if (len > write_size)
+ return;
+ memset(&h, 0, s1->header_size);
+ memcpy_to_queue(s, queue_idx, desc_idx, 0, &h, s1->header_size);
+ memcpy_to_queue(s, queue_idx, desc_idx, s1->header_size, buf, buf_len);
+ virtio_consume_desc(s, queue_idx, desc_idx, len);
+ qs->last_avail_idx++;
+}
+
+static void virtio_net_set_carrier(EthernetDevice *es, BOOL carrier_state)
+{
+#if 0
+ VIRTIODevice *s1 = es->device_opaque;
+ VIRTIONetDevice *s = (VIRTIONetDevice *)s1;
+ int cur_carrier_state;
+
+ // printf("virtio_net_set_carrier: %d\n", carrier_state);
+ cur_carrier_state = s->common.config_space[6] & 1;
+ if (cur_carrier_state != carrier_state) {
+ s->common.config_space[6] = (carrier_state << 0);
+ virtio_config_change_notify(s1);
+ }
+#endif
+}
+
+VIRTIODevice *virtio_net_init(VIRTIOBusDef *bus, EthernetDevice *es)
+{
+ VIRTIONetDevice *s;
+
+ s = mallocz(sizeof(*s));
+ virtio_init(&s->common, bus,
+ 1, 6 + 2, virtio_net_recv_request);
+ /* VIRTIO_NET_F_MAC, VIRTIO_NET_F_STATUS */
+ s->common.device_features = (1 << 5) /* | (1 << 16) */;
+ s->common.queue[0].manual_recv = TRUE;
+ s->es = es;
+ memcpy(s->common.config_space, es->mac_addr, 6);
+ /* status */
+ s->common.config_space[6] = 0;
+ s->common.config_space[7] = 0;
+
+ s->header_size = sizeof(VIRTIONetHeader);
+
+ es->device_opaque = s;
+ es->device_can_write_packet = virtio_net_can_write_packet;
+ es->device_write_packet = virtio_net_write_packet;
+ es->device_set_carrier = virtio_net_set_carrier;
+ return (VIRTIODevice *)s;
+}
+
+/*********************************************************************/
+/* console device */
+
+typedef struct VIRTIOConsoleDevice {
+ VIRTIODevice common;
+ CharacterDevice *cs;
+} VIRTIOConsoleDevice;
+
+static int virtio_console_recv_request(VIRTIODevice *s, int queue_idx,
+ int desc_idx, int read_size,
+ int write_size)
+{
+ VIRTIOConsoleDevice *s1 = (VIRTIOConsoleDevice *)s;
+ CharacterDevice *cs = s1->cs;
+ uint8_t *buf;
+
+ if (queue_idx == 1) {
+ /* send to console */
+ buf = malloc(read_size);
+ memcpy_from_queue(s, buf, queue_idx, desc_idx, 0, read_size);
+ cs->write_data(cs->opaque, buf, read_size);
+ free(buf);
+ virtio_consume_desc(s, queue_idx, desc_idx, 0);
+ }
+ return 0;
+}
+
+BOOL virtio_console_can_write_data(VIRTIODevice *s)
+{
+ QueueState *qs = &s->queue[0];
+ uint16_t avail_idx;
+
+ if (!qs->ready)
+ return FALSE;
+ avail_idx = virtio_read16(s, qs->avail_addr + 2);
+ return qs->last_avail_idx != avail_idx;
+}
+
+int virtio_console_get_write_len(VIRTIODevice *s)
+{
+ int queue_idx = 0;
+ QueueState *qs = &s->queue[queue_idx];
+ int desc_idx;
+ int read_size, write_size;
+ uint16_t avail_idx;
+
+ if (!qs->ready)
+ return 0;
+ avail_idx = virtio_read16(s, qs->avail_addr + 2);
+ if (qs->last_avail_idx == avail_idx)
+ return 0;
+ desc_idx = virtio_read16(s, qs->avail_addr + 4 +
+ (qs->last_avail_idx & (qs->num - 1)) * 2);
+ if (get_desc_rw_size(s, &read_size, &write_size, queue_idx, desc_idx))
+ return 0;
+ return write_size;
+}
+
+int virtio_console_write_data(VIRTIODevice *s, const uint8_t *buf, int buf_len)
+{
+ int queue_idx = 0;
+ QueueState *qs = &s->queue[queue_idx];
+ int desc_idx;
+ uint16_t avail_idx;
+
+ if (!qs->ready)
+ return 0;
+ avail_idx = virtio_read16(s, qs->avail_addr + 2);
+ if (qs->last_avail_idx == avail_idx)
+ return 0;
+ desc_idx = virtio_read16(s, qs->avail_addr + 4 +
+ (qs->last_avail_idx & (qs->num - 1)) * 2);
+ memcpy_to_queue(s, queue_idx, desc_idx, 0, buf, buf_len);
+ virtio_consume_desc(s, queue_idx, desc_idx, buf_len);
+ qs->last_avail_idx++;
+ return buf_len;
+}
+
+/* send a resize event */
+void virtio_console_resize_event(VIRTIODevice *s, int width, int height)
+{
+ /* indicate the console size */
+ put_le16(s->config_space + 0, width);
+ put_le16(s->config_space + 2, height);
+
+ virtio_config_change_notify(s);
+}
+
+VIRTIODevice *virtio_console_init(VIRTIOBusDef *bus, CharacterDevice *cs)
+{
+ VIRTIOConsoleDevice *s;
+
+ s = mallocz(sizeof(*s));
+ virtio_init(&s->common, bus,
+ 3, 4, virtio_console_recv_request);
+ s->common.device_features = (1 << 0); /* VIRTIO_CONSOLE_F_SIZE */
+ s->common.queue[0].manual_recv = TRUE;
+
+ s->cs = cs;
+ return (VIRTIODevice *)s;
+}
+
+/*********************************************************************/
+/* input device */
+
+enum {
+ VIRTIO_INPUT_CFG_UNSET = 0x00,
+ VIRTIO_INPUT_CFG_ID_NAME = 0x01,
+ VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
+ VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
+ VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
+ VIRTIO_INPUT_CFG_EV_BITS = 0x11,
+ VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
+};
+
+#define VIRTIO_INPUT_EV_SYN 0x00
+#define VIRTIO_INPUT_EV_KEY 0x01
+#define VIRTIO_INPUT_EV_REL 0x02
+#define VIRTIO_INPUT_EV_ABS 0x03
+#define VIRTIO_INPUT_EV_REP 0x14
+
+#define BTN_LEFT 0x110
+#define BTN_RIGHT 0x111
+#define BTN_MIDDLE 0x112
+#define BTN_GEAR_DOWN 0x150
+#define BTN_GEAR_UP 0x151
+
+#define REL_X 0x00
+#define REL_Y 0x01
+#define REL_Z 0x02
+#define REL_WHEEL 0x08
+
+#define ABS_X 0x00
+#define ABS_Y 0x01
+#define ABS_Z 0x02
+
+typedef struct VIRTIOInputDevice {
+ VIRTIODevice common;
+ VirtioInputTypeEnum type;
+ uint32_t buttons_state;
+} VIRTIOInputDevice;
+
+static const uint16_t buttons_list[] = {
+ BTN_LEFT, BTN_RIGHT, BTN_MIDDLE
+};
+
+static int virtio_input_recv_request(VIRTIODevice *s, int queue_idx,
+ int desc_idx, int read_size,
+ int write_size)
+{
+ if (queue_idx == 1) {
+ /* led & keyboard updates */
+ // printf("%s: write_size=%d\n", __func__, write_size);
+ virtio_consume_desc(s, queue_idx, desc_idx, 0);
+ }
+ return 0;
+}
+
+/* return < 0 if could not send key event */
+static int virtio_input_queue_event(VIRTIODevice *s,
+ uint16_t type, uint16_t code,
+ uint32_t value)
+{
+ int queue_idx = 0;
+ QueueState *qs = &s->queue[queue_idx];
+ int desc_idx, buf_len;
+ uint16_t avail_idx;
+ uint8_t buf[8];
+
+ if (!qs->ready)
+ return -1;
+
+ put_le16(buf, type);
+ put_le16(buf + 2, code);
+ put_le32(buf + 4, value);
+ buf_len = 8;
+
+ avail_idx = virtio_read16(s, qs->avail_addr + 2);
+ if (qs->last_avail_idx == avail_idx)
+ return -1;
+ desc_idx = virtio_read16(s, qs->avail_addr + 4 +
+ (qs->last_avail_idx & (qs->num - 1)) * 2);
+ // printf("send: queue_idx=%d desc_idx=%d\n", queue_idx, desc_idx);
+ memcpy_to_queue(s, queue_idx, desc_idx, 0, buf, buf_len);
+ virtio_consume_desc(s, queue_idx, desc_idx, buf_len);
+ qs->last_avail_idx++;
+ return 0;
+}
+
+int virtio_input_send_key_event(VIRTIODevice *s, BOOL is_down,
+ uint16_t key_code)
+{
+ VIRTIOInputDevice *s1 = (VIRTIOInputDevice *)s;
+ int ret;
+
+ if (s1->type != VIRTIO_INPUT_TYPE_KEYBOARD)
+ return -1;
+ ret = virtio_input_queue_event(s, VIRTIO_INPUT_EV_KEY, key_code, is_down);
+ if (ret)
+ return ret;
+ return virtio_input_queue_event(s, VIRTIO_INPUT_EV_SYN, 0, 0);
+}
+
+/* also used for the tablet */
+int virtio_input_send_mouse_event(VIRTIODevice *s, int dx, int dy, int dz,
+ unsigned int buttons)
+{
+ VIRTIOInputDevice *s1 = (VIRTIOInputDevice *)s;
+ int ret, i, b, last_b;
+
+ if (s1->type != VIRTIO_INPUT_TYPE_MOUSE &&
+ s1->type != VIRTIO_INPUT_TYPE_TABLET)
+ return -1;
+ if (s1->type == VIRTIO_INPUT_TYPE_MOUSE) {
+ ret = virtio_input_queue_event(s, VIRTIO_INPUT_EV_REL, REL_X, dx);
+ if (ret != 0)
+ return ret;
+ ret = virtio_input_queue_event(s, VIRTIO_INPUT_EV_REL, REL_Y, dy);
+ if (ret != 0)
+ return ret;
+ } else {
+ ret = virtio_input_queue_event(s, VIRTIO_INPUT_EV_ABS, ABS_X, dx);
+ if (ret != 0)
+ return ret;
+ ret = virtio_input_queue_event(s, VIRTIO_INPUT_EV_ABS, ABS_Y, dy);
+ if (ret != 0)
+ return ret;
+ }
+ if (dz != 0) {
+ ret = virtio_input_queue_event(s, VIRTIO_INPUT_EV_REL, REL_WHEEL, dz);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (buttons != s1->buttons_state) {
+ for(i = 0; i < countof(buttons_list); i++) {
+ b = (buttons >> i) & 1;
+ last_b = (s1->buttons_state >> i) & 1;
+ if (b != last_b) {
+ ret = virtio_input_queue_event(s, VIRTIO_INPUT_EV_KEY,
+ buttons_list[i], b);
+ if (ret != 0)
+ return ret;
+ }
+ }
+ s1->buttons_state = buttons;
+ }
+
+ return virtio_input_queue_event(s, VIRTIO_INPUT_EV_SYN, 0, 0);
+}
+
+static void set_bit(uint8_t *tab, int k)
+{
+ tab[k >> 3] |= 1 << (k & 7);
+}
+
+static void virtio_input_config_write(VIRTIODevice *s)
+{
+ VIRTIOInputDevice *s1 = (VIRTIOInputDevice *)s;
+ uint8_t *config = s->config_space;
+ int i;
+
+ // printf("config_write: %02x %02x\n", config[0], config[1]);
+ switch(config[0]) {
+ case VIRTIO_INPUT_CFG_UNSET:
+ break;
+ case VIRTIO_INPUT_CFG_ID_NAME:
+ {
+ const char *name;
+ int len;
+ switch(s1->type) {
+ case VIRTIO_INPUT_TYPE_KEYBOARD:
+ name = "virtio_keyboard";
+ break;
+ case VIRTIO_INPUT_TYPE_MOUSE:
+ name = "virtio_mouse";
+ break;
+ case VIRTIO_INPUT_TYPE_TABLET:
+ name = "virtio_tablet";
+ break;
+ default:
+ abort();
+ }
+ len = strlen(name);
+ config[2] = len;
+ memcpy(config + 8, name, len);
+ }
+ break;
+ default:
+ case VIRTIO_INPUT_CFG_ID_SERIAL:
+ case VIRTIO_INPUT_CFG_ID_DEVIDS:
+ case VIRTIO_INPUT_CFG_PROP_BITS:
+ config[2] = 0; /* size of reply */
+ break;
+ case VIRTIO_INPUT_CFG_EV_BITS:
+ config[2] = 0;
+ switch(s1->type) {
+ case VIRTIO_INPUT_TYPE_KEYBOARD:
+ switch(config[1]) {
+ case VIRTIO_INPUT_EV_KEY:
+ config[2] = 128 / 8;
+ memset(config + 8, 0xff, 128 / 8); /* bitmap */
+ break;
+ case VIRTIO_INPUT_EV_REP: /* allow key repetition */
+ config[2] = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case VIRTIO_INPUT_TYPE_MOUSE:
+ switch(config[1]) {
+ case VIRTIO_INPUT_EV_KEY:
+ config[2] = 512 / 8;
+ memset(config + 8, 0, 512 / 8); /* bitmap */
+ for(i = 0; i < countof(buttons_list); i++)
+ set_bit(config + 8, buttons_list[i]);
+ break;
+ case VIRTIO_INPUT_EV_REL:
+ config[2] = 2;
+ config[8] = 0;
+ config[9] = 0;
+ set_bit(config + 8, REL_X);
+ set_bit(config + 8, REL_Y);
+ set_bit(config + 8, REL_WHEEL);
+ break;
+ default:
+ break;
+ }
+ break;
+ case VIRTIO_INPUT_TYPE_TABLET:
+ switch(config[1]) {
+ case VIRTIO_INPUT_EV_KEY:
+ config[2] = 512 / 8;
+ memset(config + 8, 0, 512 / 8); /* bitmap */
+ for(i = 0; i < countof(buttons_list); i++)
+ set_bit(config + 8, buttons_list[i]);
+ break;
+ case VIRTIO_INPUT_EV_REL:
+ config[2] = 2;
+ config[8] = 0;
+ config[9] = 0;
+ set_bit(config + 8, REL_WHEEL);
+ break;
+ case VIRTIO_INPUT_EV_ABS:
+ config[2] = 1;
+ config[8] = 0;
+ set_bit(config + 8, ABS_X);
+ set_bit(config + 8, ABS_Y);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ abort();
+ }
+ break;
+ case VIRTIO_INPUT_CFG_ABS_INFO:
+ if (s1->type == VIRTIO_INPUT_TYPE_TABLET && config[1] <= 1) {
+ /* for ABS_X and ABS_Y */
+ config[2] = 5 * 4;
+ put_le32(config + 8, 0); /* min */
+ put_le32(config + 12, VIRTIO_INPUT_ABS_SCALE - 1) ; /* max */
+ put_le32(config + 16, 0); /* fuzz */
+ put_le32(config + 20, 0); /* flat */
+ put_le32(config + 24, 0); /* res */
+ }
+ break;
+ }
+}
+
+VIRTIODevice *virtio_input_init(VIRTIOBusDef *bus, VirtioInputTypeEnum type)
+{
+ VIRTIOInputDevice *s;
+
+ s = mallocz(sizeof(*s));
+ virtio_init(&s->common, bus,
+ 18, 256, virtio_input_recv_request);
+ s->common.queue[0].manual_recv = TRUE;
+ s->common.device_features = 0;
+ s->common.config_write = virtio_input_config_write;
+ s->type = type;
+ return (VIRTIODevice *)s;
+}
+
+/*********************************************************************/
+/* 9p filesystem device */
+
+typedef struct {
+ struct list_head link;
+ uint32_t fid;
+ FSFile *fd;
+} FIDDesc;
+
+typedef struct VIRTIO9PDevice {
+ VIRTIODevice common;
+ FSDevice *fs;
+ int msize; /* maximum message size */
+ struct list_head fid_list; /* list of FIDDesc */
+ BOOL req_in_progress;
+} VIRTIO9PDevice;
+
+static FIDDesc *fid_find1(VIRTIO9PDevice *s, uint32_t fid)
+{
+ struct list_head *el;
+ FIDDesc *f;
+
+ list_for_each(el, &s->fid_list) {
+ f = list_entry(el, FIDDesc, link);
+ if (f->fid == fid)
+ return f;
+ }
+ return NULL;
+}
+
+static FSFile *fid_find(VIRTIO9PDevice *s, uint32_t fid)
+{
+ FIDDesc *f;
+
+ f = fid_find1(s, fid);
+ if (!f)
+ return NULL;
+ return f->fd;
+}
+
+static void fid_delete(VIRTIO9PDevice *s, uint32_t fid)
+{
+ FIDDesc *f;
+
+ f = fid_find1(s, fid);
+ if (f) {
+ s->fs->fs_delete(s->fs, f->fd);
+ list_del(&f->link);
+ free(f);
+ }
+}
+
+static void fid_set(VIRTIO9PDevice *s, uint32_t fid, FSFile *fd)
+{
+ FIDDesc *f;
+
+ f = fid_find1(s, fid);
+ if (f) {
+ s->fs->fs_delete(s->fs, f->fd);
+ f->fd = fd;
+ } else {
+ f = malloc(sizeof(*f));
+ f->fid = fid;
+ f->fd = fd;
+ list_add(&f->link, &s->fid_list);
+ }
+}
+
+#ifdef DEBUG_VIRTIO
+
+typedef struct {
+ uint8_t tag;
+ const char *name;
+} Virtio9POPName;
+
+static const Virtio9POPName virtio_9p_op_names[] = {
+ { 8, "statfs" },
+ { 12, "lopen" },
+ { 14, "lcreate" },
+ { 16, "symlink" },
+ { 18, "mknod" },
+ { 22, "readlink" },
+ { 24, "getattr" },
+ { 26, "setattr" },
+ { 30, "xattrwalk" },
+ { 40, "readdir" },
+ { 50, "fsync" },
+ { 52, "lock" },
+ { 54, "getlock" },
+ { 70, "link" },
+ { 72, "mkdir" },
+ { 74, "renameat" },
+ { 76, "unlinkat" },
+ { 100, "version" },
+ { 104, "attach" },
+ { 108, "flush" },
+ { 110, "walk" },
+ { 116, "read" },
+ { 118, "write" },
+ { 120, "clunk" },
+ { 0, NULL },
+};
+
+static const char *get_9p_op_name(int tag)
+{
+ const Virtio9POPName *p;
+ for(p = virtio_9p_op_names; p->name != NULL; p++) {
+ if (p->tag == tag)
+ return p->name;
+ }
+ return NULL;
+}
+
+#endif /* DEBUG_VIRTIO */
+
+static int marshall(VIRTIO9PDevice *s,
+ uint8_t *buf1, int max_len, const char *fmt, ...)
+{
+ va_list ap;
+ int c;
+ uint32_t val;
+ uint64_t val64;
+ uint8_t *buf, *buf_end;
+
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" ->");
+#endif
+ va_start(ap, fmt);
+ buf = buf1;
+ buf_end = buf1 + max_len;
+ for(;;) {
+ c = *fmt++;
+ if (c == '\0')
+ break;
+ switch(c) {
+ case 'b':
+ assert(buf + 1 <= buf_end);
+ val = va_arg(ap, int);
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" b=%d", val);
+#endif
+ buf[0] = val;
+ buf += 1;
+ break;
+ case 'h':
+ assert(buf + 2 <= buf_end);
+ val = va_arg(ap, int);
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" h=%d", val);
+#endif
+ put_le16(buf, val);
+ buf += 2;
+ break;
+ case 'w':
+ assert(buf + 4 <= buf_end);
+ val = va_arg(ap, int);
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" w=%d", val);
+#endif
+ put_le32(buf, val);
+ buf += 4;
+ break;
+ case 'd':
+ assert(buf + 8 <= buf_end);
+ val64 = va_arg(ap, uint64_t);
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" d=%" PRId64, val64);
+#endif
+ put_le64(buf, val64);
+ buf += 8;
+ break;
+ case 's':
+ {
+ char *str;
+ int len;
+ str = va_arg(ap, char *);
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" s=\"%s\"", str);
+#endif
+ len = strlen(str);
+ assert(len <= 65535);
+ assert(buf + 2 + len <= buf_end);
+ put_le16(buf, len);
+ buf += 2;
+ memcpy(buf, str, len);
+ buf += len;
+ }
+ break;
+ case 'Q':
+ {
+ FSQID *qid;
+ assert(buf + 13 <= buf_end);
+ qid = va_arg(ap, FSQID *);
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" Q=%d:%d:%" PRIu64, qid->type, qid->version, qid->path);
+#endif
+ buf[0] = qid->type;
+ put_le32(buf + 1, qid->version);
+ put_le64(buf + 5, qid->path);
+ buf += 13;
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+ va_end(ap);
+ return buf - buf1;
+}
+
+/* return < 0 if error */
+/* XXX: free allocated strings in case of error */
+static int unmarshall(VIRTIO9PDevice *s, int queue_idx,
+ int desc_idx, int *poffset, const char *fmt, ...)
+{
+ VIRTIODevice *s1 = (VIRTIODevice *)s;
+ va_list ap;
+ int offset, c;
+ uint8_t buf[16];
+
+ offset = *poffset;
+ va_start(ap, fmt);
+ for(;;) {
+ c = *fmt++;
+ if (c == '\0')
+ break;
+ switch(c) {
+ case 'b':
+ {
+ uint8_t *ptr;
+ if (memcpy_from_queue(s1, buf, queue_idx, desc_idx, offset, 1))
+ return -1;
+ ptr = va_arg(ap, uint8_t *);
+ *ptr = buf[0];
+ offset += 1;
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" b=%d", *ptr);
+#endif
+ }
+ break;
+ case 'h':
+ {
+ uint16_t *ptr;
+ if (memcpy_from_queue(s1, buf, queue_idx, desc_idx, offset, 2))
+ return -1;
+ ptr = va_arg(ap, uint16_t *);
+ *ptr = get_le16(buf);
+ offset += 2;
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" h=%d", *ptr);
+#endif
+ }
+ break;
+ case 'w':
+ {
+ uint32_t *ptr;
+ if (memcpy_from_queue(s1, buf, queue_idx, desc_idx, offset, 4))
+ return -1;
+ ptr = va_arg(ap, uint32_t *);
+ *ptr = get_le32(buf);
+ offset += 4;
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" w=%d", *ptr);
+#endif
+ }
+ break;
+ case 'd':
+ {
+ uint64_t *ptr;
+ if (memcpy_from_queue(s1, buf, queue_idx, desc_idx, offset, 8))
+ return -1;
+ ptr = va_arg(ap, uint64_t *);
+ *ptr = get_le64(buf);
+ offset += 8;
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" d=%" PRId64, *ptr);
+#endif
+ }
+ break;
+ case 's':
+ {
+ char *str, **ptr;
+ int len;
+
+ if (memcpy_from_queue(s1, buf, queue_idx, desc_idx, offset, 2))
+ return -1;
+ len = get_le16(buf);
+ offset += 2;
+ str = malloc(len + 1);
+ if (memcpy_from_queue(s1, str, queue_idx, desc_idx, offset, len))
+ return -1;
+ str[len] = '\0';
+ offset += len;
+ ptr = va_arg(ap, char **);
+ *ptr = str;
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P)
+ printf(" s=\"%s\"", *ptr);
+#endif
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+ va_end(ap);
+ *poffset = offset;
+ return 0;
+}
+
+static void virtio_9p_send_reply(VIRTIO9PDevice *s, int queue_idx,
+ int desc_idx, uint8_t id, uint16_t tag,
+ uint8_t *buf, int buf_len)
+{
+ uint8_t *buf1;
+ int len;
+
+#ifdef DEBUG_VIRTIO
+ if (s->common.debug & VIRTIO_DEBUG_9P) {
+ if (id == 6)
+ printf(" (error)");
+ printf("\n");
+ }
+#endif
+ len = buf_len + 7;
+ buf1 = malloc(len);
+ put_le32(buf1, len);
+ buf1[4] = id + 1;
+ put_le16(buf1 + 5, tag);
+ memcpy(buf1 + 7, buf, buf_len);
+ memcpy_to_queue((VIRTIODevice *)s, queue_idx, desc_idx, 0, buf1, len);
+ virtio_consume_desc((VIRTIODevice *)s, queue_idx, desc_idx, len);
+ free(buf1);
+}
+
+static void virtio_9p_send_error(VIRTIO9PDevice *s, int queue_idx,
+ int desc_idx, uint16_t tag, uint32_t error)
+{
+ uint8_t buf[4];
+ int buf_len;
+
+ buf_len = marshall(s, buf, sizeof(buf), "w", -error);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, 6, tag, buf, buf_len);
+}
+
+typedef struct {
+ VIRTIO9PDevice *dev;
+ int queue_idx;
+ int desc_idx;
+ uint16_t tag;
+} P9OpenInfo;
+
+static void virtio_9p_open_reply(FSDevice *fs, FSQID *qid, int err,
+ P9OpenInfo *oi)
+{
+ VIRTIO9PDevice *s = oi->dev;
+ uint8_t buf[32];
+ int buf_len;
+
+ if (err < 0) {
+ virtio_9p_send_error(s, oi->queue_idx, oi->desc_idx, oi->tag, err);
+ } else {
+ buf_len = marshall(s, buf, sizeof(buf),
+ "Qw", qid, s->msize - 24);
+ virtio_9p_send_reply(s, oi->queue_idx, oi->desc_idx, 12, oi->tag,
+ buf, buf_len);
+ }
+ free(oi);
+}
+
+static void virtio_9p_open_cb(FSDevice *fs, FSQID *qid, int err,
+ void *opaque)
+{
+ P9OpenInfo *oi = opaque;
+ VIRTIO9PDevice *s = oi->dev;
+ int queue_idx = oi->queue_idx;
+
+ virtio_9p_open_reply(fs, qid, err, oi);
+
+ s->req_in_progress = FALSE;
+
+ /* handle next requests */
+ queue_notify((VIRTIODevice *)s, queue_idx);
+}
+
+static int virtio_9p_recv_request(VIRTIODevice *s1, int queue_idx,
+ int desc_idx, int read_size,
+ int write_size)
+{
+ VIRTIO9PDevice *s = (VIRTIO9PDevice *)s1;
+ int offset, header_len;
+ uint8_t id;
+ uint16_t tag;
+ uint8_t buf[1024];
+ int buf_len, err;
+ FSDevice *fs = s->fs;
+
+ if (queue_idx != 0)
+ return 0;
+
+ if (s->req_in_progress)
+ return -1;
+
+ offset = 0;
+ header_len = 4 + 1 + 2;
+ if (memcpy_from_queue(s1, buf, queue_idx, desc_idx, offset, header_len)) {
+ tag = 0;
+ goto protocol_error;
+ }
+ //size = get_le32(buf);
+ id = buf[4];
+ tag = get_le16(buf + 5);
+ offset += header_len;
+
+#ifdef DEBUG_VIRTIO
+ if (s1->debug & VIRTIO_DEBUG_9P) {
+ const char *name;
+ name = get_9p_op_name(id);
+ printf("9p: op=");
+ if (name)
+ printf("%s", name);
+ else
+ printf("%d", id);
+ }
+#endif
+ /* Note: same subset as JOR1K */
+ switch(id) {
+ case 8: /* statfs */
+ {
+ FSStatFS st;
+
+ fs->fs_statfs(fs, &st);
+ buf_len = marshall(s, buf, sizeof(buf),
+ "wwddddddw",
+ 0,
+ st.f_bsize,
+ st.f_blocks,
+ st.f_bfree,
+ st.f_bavail,
+ st.f_files,
+ st.f_ffree,
+ 0, /* id */
+ 256 /* max filename length */
+ );
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 12: /* lopen */
+ {
+ uint32_t fid, flags;
+ FSFile *f;
+ FSQID qid;
+ P9OpenInfo *oi;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "ww", &fid, &flags))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ goto fid_not_found;
+ oi = malloc(sizeof(*oi));
+ oi->dev = s;
+ oi->queue_idx = queue_idx;
+ oi->desc_idx = desc_idx;
+ oi->tag = tag;
+ err = fs->fs_open(fs, &qid, f, flags, virtio_9p_open_cb, oi);
+ if (err <= 0) {
+ virtio_9p_open_reply(fs, &qid, err, oi);
+ } else {
+ s->req_in_progress = TRUE;
+ }
+ }
+ break;
+ case 14: /* lcreate */
+ {
+ uint32_t fid, flags, mode, gid;
+ char *name;
+ FSFile *f;
+ FSQID qid;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wswww", &fid, &name, &flags, &mode, &gid))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f) {
+ err = -P9_EPROTO;
+ } else {
+ err = fs->fs_create(fs, &qid, f, name, flags, mode, gid);
+ }
+ free(name);
+ if (err)
+ goto error;
+ buf_len = marshall(s, buf, sizeof(buf),
+ "Qw", &qid, s->msize - 24);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 16: /* symlink */
+ {
+ uint32_t fid, gid;
+ char *name, *symgt;
+ FSFile *f;
+ FSQID qid;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wssw", &fid, &name, &symgt, &gid))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f) {
+ err = -P9_EPROTO;
+ } else {
+ err = fs->fs_symlink(fs, &qid, f, name, symgt, gid);
+ }
+ free(name);
+ free(symgt);
+ if (err)
+ goto error;
+ buf_len = marshall(s, buf, sizeof(buf),
+ "Q", &qid);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 18: /* mknod */
+ {
+ uint32_t fid, mode, major, minor, gid;
+ char *name;
+ FSFile *f;
+ FSQID qid;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wswwww", &fid, &name, &mode, &major, &minor, &gid))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f) {
+ err = -P9_EPROTO;
+ } else {
+ err = fs->fs_mknod(fs, &qid, f, name, mode, major, minor, gid);
+ }
+ free(name);
+ if (err)
+ goto error;
+ buf_len = marshall(s, buf, sizeof(buf),
+ "Q", &qid);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 22: /* readlink */
+ {
+ uint32_t fid;
+ char buf1[1024];
+ FSFile *f;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "w", &fid))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f) {
+ err = -P9_EPROTO;
+ } else {
+ err = fs->fs_readlink(fs, buf1, sizeof(buf1), f);
+ }
+ if (err)
+ goto error;
+ buf_len = marshall(s, buf, sizeof(buf), "s", buf1);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 24: /* getattr */
+ {
+ uint32_t fid;
+ uint64_t mask;
+ FSFile *f;
+ FSStat st;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wd", &fid, &mask))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ goto fid_not_found;
+ err = fs->fs_stat(fs, f, &st);
+ if (err)
+ goto error;
+
+ buf_len = marshall(s, buf, sizeof(buf),
+ "dQwwwddddddddddddddd",
+ mask, &st.qid,
+ st.st_mode, st.st_uid, st.st_gid,
+ st.st_nlink, st.st_rdev, st.st_size,
+ st.st_blksize, st.st_blocks,
+ st.st_atime_sec, (uint64_t)st.st_atime_nsec,
+ st.st_mtime_sec, (uint64_t)st.st_mtime_nsec,
+ st.st_ctime_sec, (uint64_t)st.st_ctime_nsec,
+ (uint64_t)0, (uint64_t)0,
+ (uint64_t)0, (uint64_t)0);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 26: /* setattr */
+ {
+ uint32_t fid, mask, mode, uid, gid;
+ uint64_t size, atime_sec, atime_nsec, mtime_sec, mtime_nsec;
+ FSFile *f;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wwwwwddddd", &fid, &mask, &mode, &uid, &gid,
+ &size, &atime_sec, &atime_nsec,
+ &mtime_sec, &mtime_nsec))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ goto fid_not_found;
+ err = fs->fs_setattr(fs, f, mask, mode, uid, gid, size, atime_sec,
+ atime_nsec, mtime_sec, mtime_nsec);
+ if (err)
+ goto error;
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, NULL, 0);
+ }
+ break;
+ case 30: /* xattrwalk */
+ {
+ /* not supported yet */
+ err = -P9_ENOTSUP;
+ goto error;
+ }
+ break;
+ case 40: /* readdir */
+ {
+ uint32_t fid, count;
+ uint64_t offs;
+ uint8_t *buf;
+ int n;
+ FSFile *f;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wdw", &fid, &offs, &count))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ goto fid_not_found;
+ buf = malloc(count + 4);
+ n = fs->fs_readdir(fs, f, offs, buf + 4, count);
+ if (n < 0) {
+ err = n;
+ goto error;
+ }
+ put_le32(buf, n);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, n + 4);
+ free(buf);
+ }
+ break;
+ case 50: /* fsync */
+ {
+ uint32_t fid;
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "w", &fid))
+ goto protocol_error;
+ /* ignored */
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, NULL, 0);
+ }
+ break;
+ case 52: /* lock */
+ {
+ uint32_t fid;
+ FSFile *f;
+ FSLock lock;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wbwddws", &fid, &lock.type, &lock.flags,
+ &lock.start, &lock.length,
+ &lock.proc_id, &lock.client_id))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ err = -P9_EPROTO;
+ else
+ err = fs->fs_lock(fs, f, &lock);
+ free(lock.client_id);
+ if (err < 0)
+ goto error;
+ buf_len = marshall(s, buf, sizeof(buf), "b", err);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 54: /* getlock */
+ {
+ uint32_t fid;
+ FSFile *f;
+ FSLock lock;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wbddws", &fid, &lock.type,
+ &lock.start, &lock.length,
+ &lock.proc_id, &lock.client_id))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ err = -P9_EPROTO;
+ else
+ err = fs->fs_getlock(fs, f, &lock);
+ if (err < 0) {
+ free(lock.client_id);
+ goto error;
+ }
+ buf_len = marshall(s, buf, sizeof(buf), "bddws",
+ &lock.type,
+ &lock.start, &lock.length,
+ &lock.proc_id, &lock.client_id);
+ free(lock.client_id);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 70: /* link */
+ {
+ uint32_t dfid, fid;
+ char *name;
+ FSFile *f, *df;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wws", &dfid, &fid, &name))
+ goto protocol_error;
+ df = fid_find(s, dfid);
+ f = fid_find(s, fid);
+ if (!df || !f) {
+ err = -P9_EPROTO;
+ } else {
+ err = fs->fs_link(fs, df, f, name);
+ }
+ free(name);
+ if (err)
+ goto error;
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, NULL, 0);
+ }
+ break;
+ case 72: /* mkdir */
+ {
+ uint32_t fid, mode, gid;
+ char *name;
+ FSFile *f;
+ FSQID qid;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wsww", &fid, &name, &mode, &gid))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ goto fid_not_found;
+ err = fs->fs_mkdir(fs, &qid, f, name, mode, gid);
+ if (err != 0)
+ goto error;
+ buf_len = marshall(s, buf, sizeof(buf), "Q", &qid);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 74: /* renameat */
+ {
+ uint32_t fid, new_fid;
+ char *name, *new_name;
+ FSFile *f, *new_f;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wsws", &fid, &name, &new_fid, &new_name))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ new_f = fid_find(s, new_fid);
+ if (!f || !new_f) {
+ err = -P9_EPROTO;
+ } else {
+ err = fs->fs_renameat(fs, f, name, new_f, new_name);
+ }
+ free(name);
+ free(new_name);
+ if (err != 0)
+ goto error;
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, NULL, 0);
+ }
+ break;
+ case 76: /* unlinkat */
+ {
+ uint32_t fid, flags;
+ char *name;
+ FSFile *f;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wsw", &fid, &name, &flags))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f) {
+ err = -P9_EPROTO;
+ } else {
+ err = fs->fs_unlinkat(fs, f, name);
+ }
+ free(name);
+ if (err != 0)
+ goto error;
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, NULL, 0);
+ }
+ break;
+ case 100: /* version */
+ {
+ uint32_t msize;
+ char *version;
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "ws", &msize, &version))
+ goto protocol_error;
+ s->msize = msize;
+ // printf("version: msize=%d version=%s\n", msize, version);
+ free(version);
+ buf_len = marshall(s, buf, sizeof(buf), "ws", s->msize, "9P2000.L");
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 104: /* attach */
+ {
+ uint32_t fid, afid, uid;
+ char *uname, *aname;
+ FSQID qid;
+ FSFile *f;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wwssw", &fid, &afid, &uname, &aname, &uid))
+ goto protocol_error;
+ err = fs->fs_attach(fs, &f, &qid, uid, uname, aname);
+ if (err != 0)
+ goto error;
+ fid_set(s, fid, f);
+ free(uname);
+ free(aname);
+ buf_len = marshall(s, buf, sizeof(buf), "Q", &qid);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 108: /* flush */
+ {
+ uint16_t oldtag;
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "h", &oldtag))
+ goto protocol_error;
+ /* ignored */
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, NULL, 0);
+ }
+ break;
+ case 110: /* walk */
+ {
+ uint32_t fid, newfid;
+ uint16_t nwname;
+ FSQID *qids;
+ char **names;
+ FSFile *f;
+ int i;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wwh", &fid, &newfid, &nwname))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ goto fid_not_found;
+ names = mallocz(sizeof(names[0]) * nwname);
+ qids = malloc(sizeof(qids[0]) * nwname);
+ for(i = 0; i < nwname; i++) {
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "s", &names[i])) {
+ err = -P9_EPROTO;
+ goto walk_done;
+ }
+ }
+ err = fs->fs_walk(fs, &f, qids, f, nwname, names);
+ walk_done:
+ for(i = 0; i < nwname; i++) {
+ free(names[i]);
+ }
+ free(names);
+ if (err < 0) {
+ free(qids);
+ goto error;
+ }
+ buf_len = marshall(s, buf, sizeof(buf), "h", err);
+ for(i = 0; i < err; i++) {
+ buf_len += marshall(s, buf + buf_len, sizeof(buf) - buf_len,
+ "Q", &qids[i]);
+ }
+ free(qids);
+ fid_set(s, newfid, f);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 116: /* read */
+ {
+ uint32_t fid, count;
+ uint64_t offs;
+ uint8_t *buf;
+ int n;
+ FSFile *f;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wdw", &fid, &offs, &count))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ goto fid_not_found;
+ buf = malloc(count + 4);
+ n = fs->fs_read(fs, f, offs, buf + 4, count);
+ if (n < 0) {
+ err = n;
+ free(buf);
+ goto error;
+ }
+ put_le32(buf, n);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, n + 4);
+ free(buf);
+ }
+ break;
+ case 118: /* write */
+ {
+ uint32_t fid, count;
+ uint64_t offs;
+ uint8_t *buf1;
+ int n;
+ FSFile *f;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "wdw", &fid, &offs, &count))
+ goto protocol_error;
+ f = fid_find(s, fid);
+ if (!f)
+ goto fid_not_found;
+ buf1 = malloc(count);
+ if (memcpy_from_queue(s1, buf1, queue_idx, desc_idx, offset,
+ count)) {
+ free(buf1);
+ goto protocol_error;
+ }
+ n = fs->fs_write(fs, f, offs, buf1, count);
+ free(buf1);
+ if (n < 0) {
+ err = n;
+ goto error;
+ }
+ buf_len = marshall(s, buf, sizeof(buf), "w", n);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, buf, buf_len);
+ }
+ break;
+ case 120: /* clunk */
+ {
+ uint32_t fid;
+
+ if (unmarshall(s, queue_idx, desc_idx, &offset,
+ "w", &fid))
+ goto protocol_error;
+ fid_delete(s, fid);
+ virtio_9p_send_reply(s, queue_idx, desc_idx, id, tag, NULL, 0);
+ }
+ break;
+ default:
+ printf("9p: unsupported operation id=%d\n", id);
+ goto protocol_error;
+ }
+ return 0;
+ error:
+ virtio_9p_send_error(s, queue_idx, desc_idx, tag, err);
+ return 0;
+ protocol_error:
+ fid_not_found:
+ err = -P9_EPROTO;
+ goto error;
+}
+
+VIRTIODevice *virtio_9p_init(VIRTIOBusDef *bus, FSDevice *fs,
+ const char *mount_tag)
+
+{
+ VIRTIO9PDevice *s;
+ int len;
+ uint8_t *cfg;
+
+ len = strlen(mount_tag);
+ s = mallocz(sizeof(*s));
+ virtio_init(&s->common, bus,
+ 9, 2 + len, virtio_9p_recv_request);
+ s->common.device_features = 1 << 0;
+
+ /* set the mount tag */
+ cfg = s->common.config_space;
+ cfg[0] = len;
+ cfg[1] = len >> 8;
+ memcpy(cfg + 2, mount_tag, len);
+
+ s->fs = fs;
+ s->msize = 8192;
+ init_list_head(&s->fid_list);
+
+ return (VIRTIODevice *)s;
+}
+
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/virtio.h b/jslinux-2019-12-21/tinyemu-2019-12-21/virtio.h
new file mode 100644
index 0000000..d53c8c4
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/virtio.h
@@ -0,0 +1,146 @@
+/*
+ * VIRTIO driver
+ *
+ * Copyright (c) 2016 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef VIRTIO_H
+#define VIRTIO_H
+
+#include <sys/select.h>
+
+#include "iomem.h"
+#include "pci.h"
+
+#define VIRTIO_PAGE_SIZE 4096
+
+#if defined(EMSCRIPTEN)
+#define VIRTIO_ADDR_BITS 32
+#else
+#define VIRTIO_ADDR_BITS 64
+#endif
+
+#if VIRTIO_ADDR_BITS == 64
+typedef uint64_t virtio_phys_addr_t;
+#else
+typedef uint32_t virtio_phys_addr_t;
+#endif
+
+typedef struct {
+ /* PCI only: */
+ PCIBus *pci_bus;
+ /* MMIO only: */
+ PhysMemoryMap *mem_map;
+ uint64_t addr;
+ IRQSignal *irq;
+} VIRTIOBusDef;
+
+typedef struct VIRTIODevice VIRTIODevice;
+
+#define VIRTIO_DEBUG_IO (1 << 0)
+#define VIRTIO_DEBUG_9P (1 << 1)
+
+void virtio_set_debug(VIRTIODevice *s, int debug_flags);
+
+/* block device */
+
+typedef void BlockDeviceCompletionFunc(void *opaque, int ret);
+
+typedef struct BlockDevice BlockDevice;
+
+struct BlockDevice {
+ int64_t (*get_sector_count)(BlockDevice *bs);
+ int (*read_async)(BlockDevice *bs,
+ uint64_t sector_num, uint8_t *buf, int n,
+ BlockDeviceCompletionFunc *cb, void *opaque);
+ int (*write_async)(BlockDevice *bs,
+ uint64_t sector_num, const uint8_t *buf, int n,
+ BlockDeviceCompletionFunc *cb, void *opaque);
+ void *opaque;
+};
+
+VIRTIODevice *virtio_block_init(VIRTIOBusDef *bus, BlockDevice *bs);
+
+/* network device */
+
+typedef struct EthernetDevice EthernetDevice;
+
+struct EthernetDevice {
+ uint8_t mac_addr[6]; /* mac address of the interface */
+ void (*write_packet)(EthernetDevice *net,
+ const uint8_t *buf, int len);
+ void *opaque;
+#if !defined(EMSCRIPTEN)
+ void (*select_fill)(EthernetDevice *net, int *pfd_max,
+ fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int *pdelay);
+ void (*select_poll)(EthernetDevice *net,
+ fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int select_ret);
+#endif
+ /* the following is set by the device */
+ void *device_opaque;
+ BOOL (*device_can_write_packet)(EthernetDevice *net);
+ void (*device_write_packet)(EthernetDevice *net,
+ const uint8_t *buf, int len);
+ void (*device_set_carrier)(EthernetDevice *net, BOOL carrier_state);
+};
+
+VIRTIODevice *virtio_net_init(VIRTIOBusDef *bus, EthernetDevice *es);
+
+/* console device */
+
+typedef struct {
+ void *opaque;
+ void (*write_data)(void *opaque, const uint8_t *buf, int len);
+ int (*read_data)(void *opaque, uint8_t *buf, int len);
+} CharacterDevice;
+
+VIRTIODevice *virtio_console_init(VIRTIOBusDef *bus, CharacterDevice *cs);
+BOOL virtio_console_can_write_data(VIRTIODevice *s);
+int virtio_console_get_write_len(VIRTIODevice *s);
+int virtio_console_write_data(VIRTIODevice *s, const uint8_t *buf, int buf_len);
+void virtio_console_resize_event(VIRTIODevice *s, int width, int height);
+
+/* input device */
+
+typedef enum {
+ VIRTIO_INPUT_TYPE_KEYBOARD,
+ VIRTIO_INPUT_TYPE_MOUSE,
+ VIRTIO_INPUT_TYPE_TABLET,
+} VirtioInputTypeEnum;
+
+#define VIRTIO_INPUT_ABS_SCALE 32768
+
+int virtio_input_send_key_event(VIRTIODevice *s, BOOL is_down,
+ uint16_t key_code);
+int virtio_input_send_mouse_event(VIRTIODevice *s, int dx, int dy, int dz,
+ unsigned int buttons);
+
+VIRTIODevice *virtio_input_init(VIRTIOBusDef *bus, VirtioInputTypeEnum type);
+
+/* 9p filesystem device */
+
+#include "fs.h"
+
+VIRTIODevice *virtio_9p_init(VIRTIOBusDef *bus, FSDevice *fs,
+ const char *mount_tag);
+
+#endif /* VIRTIO_H */
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/vmmouse.c b/jslinux-2019-12-21/tinyemu-2019-12-21/vmmouse.c
new file mode 100644
index 0000000..927e510
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/vmmouse.c
@@ -0,0 +1,162 @@
+/*
+ * VM mouse emulation
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "ps2.h"
+
+#define VMPORT_MAGIC 0x564D5868
+
+#define REG_EAX 0
+#define REG_EBX 1
+#define REG_ECX 2
+#define REG_EDX 3
+#define REG_ESI 4
+#define REG_EDI 5
+
+#define FIFO_SIZE (4 * 16)
+
+struct VMMouseState {
+ PS2MouseState *ps2_mouse;
+ int fifo_count, fifo_rindex, fifo_windex;
+ BOOL enabled;
+ BOOL absolute;
+ uint32_t fifo_buf[FIFO_SIZE];
+};
+
+static void put_queue(VMMouseState *s, uint32_t val)
+{
+ if (s->fifo_count >= FIFO_SIZE)
+ return;
+ s->fifo_buf[s->fifo_windex] = val;
+ if (++s->fifo_windex == FIFO_SIZE)
+ s->fifo_windex = 0;
+ s->fifo_count++;
+}
+
+static void read_data(VMMouseState *s, uint32_t *regs, int size)
+{
+ int i;
+ if (size > 6 || size > s->fifo_count) {
+ // printf("vmmouse: read error req=%d count=%d\n", size, s->fifo_count);
+ s->enabled = FALSE;
+ return;
+ }
+ for(i = 0; i < size; i++) {
+ regs[i] = s->fifo_buf[s->fifo_rindex];
+ if (++s->fifo_rindex == FIFO_SIZE)
+ s->fifo_rindex = 0;
+ }
+ s->fifo_count -= size;
+}
+
+void vmmouse_send_mouse_event(VMMouseState *s, int x, int y, int dz,
+ int buttons)
+{
+ int state;
+
+ if (!s->enabled) {
+ ps2_mouse_event(s->ps2_mouse, x, y, dz, buttons);
+ return;
+ }
+
+ if ((s->fifo_count + 4) > FIFO_SIZE)
+ return;
+
+ state = 0;
+ if (buttons & 1)
+ state |= 0x20;
+ if (buttons & 2)
+ state |= 0x10;
+ if (buttons & 4)
+ state |= 0x08;
+ if (s->absolute) {
+ /* range = 0 ... 65535 */
+ x *= 2;
+ y *= 2;
+ }
+
+ put_queue(s, state);
+ put_queue(s, x);
+ put_queue(s, y);
+ put_queue(s, -dz);
+
+ /* send PS/2 mouse event */
+ ps2_mouse_event(s->ps2_mouse, 1, 0, 0, 0);
+}
+
+void vmmouse_handler(VMMouseState *s, uint32_t *regs)
+{
+ uint32_t cmd;
+
+ cmd = regs[REG_ECX] & 0xff;
+ switch(cmd) {
+ case 10: /* get version */
+ regs[REG_EBX] = VMPORT_MAGIC;
+ break;
+ case 39: /* VMMOUSE_DATA */
+ read_data(s, regs, regs[REG_EBX]);
+ break;
+ case 40: /* VMMOUSE_STATUS */
+ regs[REG_EAX] = ((s->enabled ? 0 : 0xffff) << 16) | s->fifo_count;
+ break;
+ case 41: /* VMMOUSE_COMMAND */
+ switch(regs[REG_EBX]) {
+ case 0x45414552: /* read id */
+ if (s->fifo_count < FIFO_SIZE) {
+ put_queue(s, 0x3442554a);
+ s->enabled = TRUE;
+ }
+ break;
+ case 0x000000f5: /* disable */
+ s->enabled = FALSE;
+ break;
+ case 0x4c455252: /* set relative */
+ s->absolute = 0;
+ break;
+ case 0x53424152: /* set absolute */
+ s->absolute = 1;
+ break;
+ }
+ break;
+ }
+}
+
+BOOL vmmouse_is_absolute(VMMouseState *s)
+{
+ return s->absolute;
+}
+
+VMMouseState *vmmouse_init(PS2MouseState *ps2_mouse)
+{
+ VMMouseState *s;
+ s = mallocz(sizeof(*s));
+ s->ps2_mouse = ps2_mouse;
+ return s;
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/x86_cpu.c b/jslinux-2019-12-21/tinyemu-2019-12-21/x86_cpu.c
new file mode 100644
index 0000000..e599ab8
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/x86_cpu.c
@@ -0,0 +1,96 @@
+/*
+ * x86 CPU emulator stub
+ *
+ * Copyright (c) 2011-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "x86_cpu.h"
+
+X86CPUState *x86_cpu_init(PhysMemoryMap *mem_map)
+{
+ fprintf(stderr, "x86 emulator is not supported\n");
+ exit(1);
+}
+
+void x86_cpu_end(X86CPUState *s)
+{
+}
+
+void x86_cpu_interp(X86CPUState *s, int max_cycles1)
+{
+}
+
+void x86_cpu_set_irq(X86CPUState *s, BOOL set)
+{
+}
+
+void x86_cpu_set_reg(X86CPUState *s, int reg, uint32_t val)
+{
+}
+
+uint32_t x86_cpu_get_reg(X86CPUState *s, int reg)
+{
+ return 0;
+}
+
+void x86_cpu_set_seg(X86CPUState *s, int seg, const X86CPUSeg *sd)
+{
+}
+
+void x86_cpu_set_get_hard_intno(X86CPUState *s,
+ int (*get_hard_intno)(void *opaque),
+ void *opaque)
+{
+}
+
+void x86_cpu_set_get_tsc(X86CPUState *s,
+ uint64_t (*get_tsc)(void *opaque),
+ void *opaque)
+{
+}
+
+void x86_cpu_set_port_io(X86CPUState *s,
+ DeviceReadFunc *port_read, DeviceWriteFunc *port_write,
+ void *opaque)
+{
+}
+
+int64_t x86_cpu_get_cycles(X86CPUState *s)
+{
+ return 0;
+}
+
+BOOL x86_cpu_get_power_down(X86CPUState *s)
+{
+ return FALSE;
+}
+
+void x86_cpu_flush_tlb_write_range_ram(X86CPUState *s,
+ uint8_t *ram_ptr, size_t ram_size)
+{
+}
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/x86_cpu.h b/jslinux-2019-12-21/tinyemu-2019-12-21/x86_cpu.h
new file mode 100644
index 0000000..254f7f5
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/x86_cpu.h
@@ -0,0 +1,70 @@
+/*
+ * x86 CPU emulator
+ *
+ * Copyright (c) 2011-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "iomem.h"
+
+typedef struct X86CPUState X86CPUState;
+
+/* get_reg/set_reg additional constants */
+#define X86_CPU_REG_EIP 8
+#define X86_CPU_REG_CR0 9
+#define X86_CPU_REG_CR2 10
+
+#define X86_CPU_SEG_ES 0
+#define X86_CPU_SEG_CS 1
+#define X86_CPU_SEG_SS 2
+#define X86_CPU_SEG_DS 3
+#define X86_CPU_SEG_FS 4
+#define X86_CPU_SEG_GS 5
+#define X86_CPU_SEG_LDT 6
+#define X86_CPU_SEG_TR 7
+#define X86_CPU_SEG_GDT 8
+#define X86_CPU_SEG_IDT 9
+
+typedef struct {
+ uint16_t sel;
+ uint16_t flags;
+ uint32_t base;
+ uint32_t limit;
+} X86CPUSeg;
+
+X86CPUState *x86_cpu_init(PhysMemoryMap *mem_map);
+void x86_cpu_end(X86CPUState *s);
+void x86_cpu_interp(X86CPUState *s, int max_cycles1);
+void x86_cpu_set_irq(X86CPUState *s, BOOL set);
+void x86_cpu_set_reg(X86CPUState *s, int reg, uint32_t val);
+uint32_t x86_cpu_get_reg(X86CPUState *s, int reg);
+void x86_cpu_set_seg(X86CPUState *s, int seg, const X86CPUSeg *sd);
+void x86_cpu_set_get_hard_intno(X86CPUState *s,
+ int (*get_hard_intno)(void *opaque),
+ void *opaque);
+void x86_cpu_set_get_tsc(X86CPUState *s,
+ uint64_t (*get_tsc)(void *opaque),
+ void *opaque);
+void x86_cpu_set_port_io(X86CPUState *s,
+ DeviceReadFunc *port_read, DeviceWriteFunc *port_write,
+ void *opaque);
+int64_t x86_cpu_get_cycles(X86CPUState *s);
+BOOL x86_cpu_get_power_down(X86CPUState *s);
+void x86_cpu_flush_tlb_write_range_ram(X86CPUState *s,
+ uint8_t *ram_ptr, size_t ram_size);
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/x86_machine.c b/jslinux-2019-12-21/tinyemu-2019-12-21/x86_machine.c
new file mode 100644
index 0000000..db8f6bb
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/x86_machine.c
@@ -0,0 +1,2569 @@
+/*
+ * PC emulator
+ *
+ * Copyright (c) 2011-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "virtio.h"
+#include "x86_cpu.h"
+#include "machine.h"
+#include "pci.h"
+#include "ide.h"
+#include "ps2.h"
+
+#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
+#define USE_KVM
+#endif
+
+#ifdef USE_KVM
+#include <linux/kvm.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <sys/time.h>
+#endif
+
+//#define DEBUG_BIOS
+//#define DUMP_IOPORT
+
+/***********************************************************/
+/* cmos emulation */
+
+//#define DEBUG_CMOS
+
+#define RTC_SECONDS 0
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES 2
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS 4
+#define RTC_HOURS_ALARM 5
+#define RTC_ALARM_DONT_CARE 0xC0
+
+#define RTC_DAY_OF_WEEK 6
+#define RTC_DAY_OF_MONTH 7
+#define RTC_MONTH 8
+#define RTC_YEAR 9
+
+#define RTC_REG_A 10
+#define RTC_REG_B 11
+#define RTC_REG_C 12
+#define RTC_REG_D 13
+
+#define REG_A_UIP 0x80
+
+#define REG_B_SET 0x80
+#define REG_B_PIE 0x40
+#define REG_B_AIE 0x20
+#define REG_B_UIE 0x10
+
+typedef struct {
+ uint8_t cmos_index;
+ uint8_t cmos_data[128];
+ IRQSignal *irq;
+ BOOL use_local_time;
+ /* used for the periodic irq */
+ uint32_t irq_timeout;
+ uint32_t irq_period;
+} CMOSState;
+
+static void cmos_write(void *opaque, uint32_t offset,
+ uint32_t data, int size_log2);
+static uint32_t cmos_read(void *opaque, uint32_t offset, int size_log2);
+
+static int to_bcd(CMOSState *s, unsigned int a)
+{
+ if (s->cmos_data[RTC_REG_B] & 0x04) {
+ return a;
+ } else {
+ return ((a / 10) << 4) | (a % 10);
+ }
+}
+
+static void cmos_update_time(CMOSState *s, BOOL set_century)
+{
+ struct timeval tv;
+ struct tm tm;
+ time_t ti;
+ int val;
+
+ gettimeofday(&tv, NULL);
+ ti = tv.tv_sec;
+ if (s->use_local_time) {
+ localtime_r(&ti, &tm);
+ } else {
+ gmtime_r(&ti, &tm);
+ }
+
+ s->cmos_data[RTC_SECONDS] = to_bcd(s, tm.tm_sec);
+ s->cmos_data[RTC_MINUTES] = to_bcd(s, tm.tm_min);
+ if (s->cmos_data[RTC_REG_B] & 0x02) {
+ s->cmos_data[RTC_HOURS] = to_bcd(s, tm.tm_hour);
+ } else {
+ s->cmos_data[RTC_HOURS] = to_bcd(s, tm.tm_hour % 12);
+ if (tm.tm_hour >= 12)
+ s->cmos_data[RTC_HOURS] |= 0x80;
+ }
+ s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm.tm_wday);
+ s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm.tm_mday);
+ s->cmos_data[RTC_MONTH] = to_bcd(s, tm.tm_mon + 1);
+ s->cmos_data[RTC_YEAR] = to_bcd(s, tm.tm_year % 100);
+
+ if (set_century) {
+ /* not set by the hardware, but easier to do it here */
+ val = to_bcd(s, (tm.tm_year / 100) + 19);
+ s->cmos_data[0x32] = val;
+ s->cmos_data[0x37] = val;
+ }
+
+ /* update in progress flag: 8/32768 seconds after change */
+ if (tv.tv_usec < 244) {
+ s->cmos_data[RTC_REG_A] |= REG_A_UIP;
+ } else {
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+ }
+}
+
+CMOSState *cmos_init(PhysMemoryMap *port_map, int addr,
+ IRQSignal *irq, BOOL use_local_time)
+{
+ CMOSState *s;
+
+ s = mallocz(sizeof(*s));
+ s->use_local_time = use_local_time;
+
+ s->cmos_index = 0;
+
+ s->cmos_data[RTC_REG_A] = 0x26;
+ s->cmos_data[RTC_REG_B] = 0x02;
+ s->cmos_data[RTC_REG_C] = 0x00;
+ s->cmos_data[RTC_REG_D] = 0x80;
+
+ cmos_update_time(s, TRUE);
+
+ s->irq = irq;
+
+ cpu_register_device(port_map, addr, 2, s, cmos_read, cmos_write,
+ DEVIO_SIZE8);
+ return s;
+}
+
+#define CMOS_FREQ 32768
+
+static uint32_t cmos_get_timer(CMOSState *s)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint32_t)ts.tv_sec * CMOS_FREQ +
+ ((uint64_t)ts.tv_nsec * CMOS_FREQ / 1000000000);
+}
+
+static void cmos_update_timer(CMOSState *s)
+{
+ int period_code;
+
+ period_code = s->cmos_data[RTC_REG_A] & 0x0f;
+ if ((s->cmos_data[RTC_REG_B] & REG_B_PIE) &&
+ period_code != 0) {
+ if (period_code <= 2)
+ period_code += 7;
+ s->irq_period = 1 << (period_code - 1);
+ s->irq_timeout = (cmos_get_timer(s) + s->irq_period) &
+ ~(s->irq_period - 1);
+ }
+}
+
+/* XXX: could return a delay, but we don't need high precision
+ (Windows 2000 uses it for delay calibration) */
+static void cmos_update_irq(CMOSState *s)
+{
+ uint32_t d;
+ if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
+ d = cmos_get_timer(s) - s->irq_timeout;
+ if ((int32_t)d >= 0) {
+ /* this is not what the real RTC does. Here we sent the IRQ
+ immediately */
+ s->cmos_data[RTC_REG_C] |= 0xc0;
+ set_irq(s->irq, 1);
+ /* update for the next irq */
+ s->irq_timeout += s->irq_period;
+ }
+ }
+}
+
+static void cmos_write(void *opaque, uint32_t offset,
+ uint32_t data, int size_log2)
+{
+ CMOSState *s = opaque;
+
+ if (offset == 0) {
+ s->cmos_index = data & 0x7f;
+ } else {
+#ifdef DEBUG_CMOS
+ printf("cmos_write: reg=0x%02x val=0x%02x\n", s->cmos_index, data);
+#endif
+ switch(s->cmos_index) {
+ case RTC_REG_A:
+ s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
+ (s->cmos_data[RTC_REG_A] & REG_A_UIP);
+ cmos_update_timer(s);
+ break;
+ case RTC_REG_B:
+ s->cmos_data[s->cmos_index] = data;
+ cmos_update_timer(s);
+ break;
+ default:
+ s->cmos_data[s->cmos_index] = data;
+ break;
+ }
+ }
+}
+
+static uint32_t cmos_read(void *opaque, uint32_t offset, int size_log2)
+{
+ CMOSState *s = opaque;
+ int ret;
+
+ if (offset == 0) {
+ return 0xff;
+ } else {
+ switch(s->cmos_index) {
+ case RTC_SECONDS:
+ case RTC_MINUTES:
+ case RTC_HOURS:
+ case RTC_DAY_OF_WEEK:
+ case RTC_DAY_OF_MONTH:
+ case RTC_MONTH:
+ case RTC_YEAR:
+ case RTC_REG_A:
+ cmos_update_time(s, FALSE);
+ ret = s->cmos_data[s->cmos_index];
+ break;
+ case RTC_REG_C:
+ ret = s->cmos_data[s->cmos_index];
+ s->cmos_data[RTC_REG_C] = 0x00;
+ set_irq(s->irq, 0);
+ break;
+ default:
+ ret = s->cmos_data[s->cmos_index];
+ }
+#ifdef DEBUG_CMOS
+ printf("cmos_read: reg=0x%02x val=0x%02x\n", s->cmos_index, ret);
+#endif
+ return ret;
+ }
+}
+
+/***********************************************************/
+/* 8259 pic emulation */
+
+//#define DEBUG_PIC
+
+typedef void PICUpdateIRQFunc(void *opaque);
+
+typedef struct {
+ uint8_t last_irr; /* edge detection */
+ uint8_t irr; /* interrupt request register */
+ uint8_t imr; /* interrupt mask register */
+ uint8_t isr; /* interrupt service register */
+ uint8_t priority_add; /* used to compute irq priority */
+ uint8_t irq_base;
+ uint8_t read_reg_select;
+ uint8_t special_mask;
+ uint8_t init_state;
+ uint8_t auto_eoi;
+ uint8_t rotate_on_autoeoi;
+ uint8_t init4; /* true if 4 byte init */
+ uint8_t elcr; /* PIIX edge/trigger selection*/
+ uint8_t elcr_mask;
+ PICUpdateIRQFunc *update_irq;
+ void *opaque;
+} PICState;
+
+static void pic_reset(PICState *s);
+static void pic_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2);
+static uint32_t pic_read(void *opaque, uint32_t offset, int size_log2);
+static void pic_elcr_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2);
+static uint32_t pic_elcr_read(void *opaque, uint32_t offset, int size_log2);
+
+PICState *pic_init(PhysMemoryMap *port_map, int port, int elcr_port,
+ int elcr_mask,
+ PICUpdateIRQFunc *update_irq, void *opaque)
+{
+ PICState *s;
+
+ s = mallocz(sizeof(*s));
+ s->elcr_mask = elcr_mask;
+ s->update_irq = update_irq;
+ s->opaque = opaque;
+ cpu_register_device(port_map, port, 2, s,
+ pic_read, pic_write, DEVIO_SIZE8);
+ cpu_register_device(port_map, elcr_port, 1, s,
+ pic_elcr_read, pic_elcr_write, DEVIO_SIZE8);
+ pic_reset(s);
+ return s;
+}
+
+static void pic_reset(PICState *s)
+{
+ /* all 8 bit registers */
+ s->last_irr = 0; /* edge detection */
+ s->irr = 0; /* interrupt request register */
+ s->imr = 0; /* interrupt mask register */
+ s->isr = 0; /* interrupt service register */
+ s->priority_add = 0; /* used to compute irq priority */
+ s->irq_base = 0;
+ s->read_reg_select = 0;
+ s->special_mask = 0;
+ s->init_state = 0;
+ s->auto_eoi = 0;
+ s->rotate_on_autoeoi = 0;
+ s->init4 = 0; /* true if 4 byte init */
+}
+
+/* set irq level. If an edge is detected, then the IRR is set to 1 */
+static void pic_set_irq1(PICState *s, int irq, int level)
+{
+ int mask;
+ mask = 1 << irq;
+ if (s->elcr & mask) {
+ /* level triggered */
+ if (level) {
+ s->irr |= mask;
+ s->last_irr |= mask;
+ } else {
+ s->irr &= ~mask;
+ s->last_irr &= ~mask;
+ }
+ } else {
+ /* edge triggered */
+ if (level) {
+ if ((s->last_irr & mask) == 0)
+ s->irr |= mask;
+ s->last_irr |= mask;
+ } else {
+ s->last_irr &= ~mask;
+ }
+ }
+}
+
+static int pic_get_priority(PICState *s, int mask)
+{
+ int priority;
+ if (mask == 0)
+ return -1;
+ priority = 7;
+ while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
+ priority--;
+ return priority;
+}
+
+/* return the pic wanted interrupt. return -1 if none */
+static int pic_get_irq(PICState *s)
+{
+ int mask, cur_priority, priority;
+
+ mask = s->irr & ~s->imr;
+ priority = pic_get_priority(s, mask);
+ if (priority < 0)
+ return -1;
+ /* compute current priority */
+ cur_priority = pic_get_priority(s, s->isr);
+ if (priority > cur_priority) {
+ /* higher priority found: an irq should be generated */
+ return priority;
+ } else {
+ return -1;
+ }
+}
+
+/* acknowledge interrupt 'irq' */
+static void pic_intack(PICState *s, int irq)
+{
+ if (s->auto_eoi) {
+ if (s->rotate_on_autoeoi)
+ s->priority_add = (irq + 1) & 7;
+ } else {
+ s->isr |= (1 << irq);
+ }
+ /* We don't clear a level sensitive interrupt here */
+ if (!(s->elcr & (1 << irq)))
+ s->irr &= ~(1 << irq);
+}
+
+static void pic_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ PICState *s = opaque;
+ int priority, addr;
+
+ addr = offset & 1;
+#ifdef DEBUG_PIC
+ console.log("pic_write: addr=" + toHex2(addr) + " val=" + toHex2(val));
+#endif
+ if (addr == 0) {
+ if (val & 0x10) {
+ /* init */
+ pic_reset(s);
+ s->init_state = 1;
+ s->init4 = val & 1;
+ if (val & 0x02)
+ abort(); /* "single mode not supported" */
+ if (val & 0x08)
+ abort(); /* "level sensitive irq not supported" */
+ } else if (val & 0x08) {
+ if (val & 0x02)
+ s->read_reg_select = val & 1;
+ if (val & 0x40)
+ s->special_mask = (val >> 5) & 1;
+ } else {
+ switch(val) {
+ case 0x00:
+ case 0x80:
+ s->rotate_on_autoeoi = val >> 7;
+ break;
+ case 0x20: /* end of interrupt */
+ case 0xa0:
+ priority = pic_get_priority(s, s->isr);
+ if (priority >= 0) {
+ s->isr &= ~(1 << ((priority + s->priority_add) & 7));
+ }
+ if (val == 0xa0)
+ s->priority_add = (s->priority_add + 1) & 7;
+ break;
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ priority = val & 7;
+ s->isr &= ~(1 << priority);
+ break;
+ case 0xc0:
+ case 0xc1:
+ case 0xc2:
+ case 0xc3:
+ case 0xc4:
+ case 0xc5:
+ case 0xc6:
+ case 0xc7:
+ s->priority_add = (val + 1) & 7;
+ break;
+ case 0xe0:
+ case 0xe1:
+ case 0xe2:
+ case 0xe3:
+ case 0xe4:
+ case 0xe5:
+ case 0xe6:
+ case 0xe7:
+ priority = val & 7;
+ s->isr &= ~(1 << priority);
+ s->priority_add = (priority + 1) & 7;
+ break;
+ }
+ }
+ } else {
+ switch(s->init_state) {
+ case 0:
+ /* normal mode */
+ s->imr = val;
+ s->update_irq(s->opaque);
+ break;
+ case 1:
+ s->irq_base = val & 0xf8;
+ s->init_state = 2;
+ break;
+ case 2:
+ if (s->init4) {
+ s->init_state = 3;
+ } else {
+ s->init_state = 0;
+ }
+ break;
+ case 3:
+ s->auto_eoi = (val >> 1) & 1;
+ s->init_state = 0;
+ break;
+ }
+ }
+}
+
+static uint32_t pic_read(void *opaque, uint32_t offset, int size_log2)
+{
+ PICState *s = opaque;
+ int addr, ret;
+
+ addr = offset & 1;
+ if (addr == 0) {
+ if (s->read_reg_select)
+ ret = s->isr;
+ else
+ ret = s->irr;
+ } else {
+ ret = s->imr;
+ }
+#ifdef DEBUG_PIC
+ console.log("pic_read: addr=" + toHex2(addr1) + " val=" + toHex2(ret));
+#endif
+ return ret;
+}
+
+static void pic_elcr_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ PICState *s = opaque;
+ s->elcr = val & s->elcr_mask;
+}
+
+static uint32_t pic_elcr_read(void *opaque, uint32_t offset, int size_log2)
+{
+ PICState *s = opaque;
+ return s->elcr;
+}
+
+typedef struct {
+ PICState *pics[2];
+ int irq_requested;
+ void (*cpu_set_irq)(void *opaque, int level);
+ void *opaque;
+#if defined(DEBUG_PIC)
+ uint8_t irq_level[16];
+#endif
+ IRQSignal *irqs;
+} PIC2State;
+
+static void pic2_update_irq(void *opaque);
+static void pic2_set_irq(void *opaque, int irq, int level);
+
+PIC2State *pic2_init(PhysMemoryMap *port_map, uint32_t addr0, uint32_t addr1,
+ uint32_t elcr_addr0, uint32_t elcr_addr1,
+ void (*cpu_set_irq)(void *opaque, int level),
+ void *opaque, IRQSignal *irqs)
+{
+ PIC2State *s;
+ int i;
+
+ s = mallocz(sizeof(*s));
+
+ for(i = 0; i < 16; i++) {
+ irq_init(&irqs[i], pic2_set_irq, s, i);
+ }
+ s->cpu_set_irq = cpu_set_irq;
+ s->opaque = opaque;
+ s->pics[0] = pic_init(port_map, addr0, elcr_addr0, 0xf8, pic2_update_irq, s);
+ s->pics[1] = pic_init(port_map, addr1, elcr_addr1, 0xde, pic2_update_irq, s);
+ s->irq_requested = 0;
+ return s;
+}
+
+void pic2_set_elcr(PIC2State *s, const uint8_t *elcr)
+{
+ int i;
+ for(i = 0; i < 2; i++) {
+ s->pics[i]->elcr = elcr[i] & s->pics[i]->elcr_mask;
+ }
+}
+
+/* raise irq to CPU if necessary. must be called every time the active
+ irq may change */
+static void pic2_update_irq(void *opaque)
+{
+ PIC2State *s = opaque;
+ int irq2, irq;
+
+ /* first look at slave pic */
+ irq2 = pic_get_irq(s->pics[1]);
+ if (irq2 >= 0) {
+ /* if irq request by slave pic, signal master PIC */
+ pic_set_irq1(s->pics[0], 2, 1);
+ pic_set_irq1(s->pics[0], 2, 0);
+ }
+ /* look at requested irq */
+ irq = pic_get_irq(s->pics[0]);
+#if 0
+ console.log("irr=" + toHex2(s->pics[0].irr) + " imr=" + toHex2(s->pics[0].imr) + " isr=" + toHex2(s->pics[0].isr) + " irq="+ irq);
+#endif
+ if (irq >= 0) {
+ /* raise IRQ request on the CPU */
+ s->cpu_set_irq(s->opaque, 1);
+ } else {
+ /* lower irq */
+ s->cpu_set_irq(s->opaque, 0);
+ }
+}
+
+static void pic2_set_irq(void *opaque, int irq, int level)
+{
+ PIC2State *s = opaque;
+#if defined(DEBUG_PIC)
+ if (irq != 0 && level != s->irq_level[irq]) {
+ console.log("pic_set_irq: irq=" + irq + " level=" + level);
+ s->irq_level[irq] = level;
+ }
+#endif
+ pic_set_irq1(s->pics[irq >> 3], irq & 7, level);
+ pic2_update_irq(s);
+}
+
+/* called from the CPU to get the hardware interrupt number */
+static int pic2_get_hard_intno(PIC2State *s)
+{
+ int irq, irq2, intno;
+
+ irq = pic_get_irq(s->pics[0]);
+ if (irq >= 0) {
+ pic_intack(s->pics[0], irq);
+ if (irq == 2) {
+ irq2 = pic_get_irq(s->pics[1]);
+ if (irq2 >= 0) {
+ pic_intack(s->pics[1], irq2);
+ } else {
+ /* spurious IRQ on slave controller */
+ irq2 = 7;
+ }
+ intno = s->pics[1]->irq_base + irq2;
+ irq = irq2 + 8;
+ } else {
+ intno = s->pics[0]->irq_base + irq;
+ }
+ } else {
+ /* spurious IRQ on host controller */
+ irq = 7;
+ intno = s->pics[0]->irq_base + irq;
+ }
+ pic2_update_irq(s);
+
+#if defined(DEBUG_PIC)
+ if (irq != 0 && irq != 14)
+ printf("pic_interrupt: irq=%d\n", irq);
+#endif
+ return intno;
+}
+
+/***********************************************************/
+/* 8253 PIT emulation */
+
+#define PIT_FREQ 1193182
+
+#define RW_STATE_LSB 0
+#define RW_STATE_MSB 1
+#define RW_STATE_WORD0 2
+#define RW_STATE_WORD1 3
+#define RW_STATE_LATCHED_WORD0 4
+#define RW_STATE_LATCHED_WORD1 5
+
+//#define DEBUG_PIT
+
+typedef int64_t PITGetTicksFunc(void *opaque);
+
+typedef struct PITState PITState;
+
+typedef struct {
+ PITState *pit_state;
+ uint32_t count;
+ uint32_t latched_count;
+ uint8_t rw_state;
+ uint8_t mode;
+ uint8_t bcd;
+ uint8_t gate;
+ int64_t count_load_time;
+ int64_t last_irq_time;
+} PITChannel;
+
+struct PITState {
+ PITChannel pit_channels[3];
+ uint8_t speaker_data_on;
+ PITGetTicksFunc *get_ticks;
+ IRQSignal *irq;
+ void *opaque;
+};
+
+static void pit_load_count(PITChannel *pc, int val);
+static void pit_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2);
+static uint32_t pit_read(void *opaque, uint32_t offset, int size_log2);
+static void speaker_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2);
+static uint32_t speaker_read(void *opaque, uint32_t offset, int size_log2);
+
+PITState *pit_init(PhysMemoryMap *port_map, int addr0, int addr1,
+ IRQSignal *irq,
+ PITGetTicksFunc *get_ticks, void *opaque)
+{
+ PITState *s;
+ PITChannel *pc;
+ int i;
+
+ s = mallocz(sizeof(*s));
+
+ s->irq = irq;
+ s->get_ticks = get_ticks;
+ s->opaque = opaque;
+
+ for(i = 0; i < 3; i++) {
+ pc = &s->pit_channels[i];
+ pc->pit_state = s;
+ pc->mode = 3;
+ pc->gate = (i != 2) >> 0;
+ pit_load_count(pc, 0);
+ }
+ s->speaker_data_on = 0;
+
+ cpu_register_device(port_map, addr0, 4, s, pit_read, pit_write,
+ DEVIO_SIZE8);
+
+ cpu_register_device(port_map, addr1, 1, s, speaker_read, speaker_write,
+ DEVIO_SIZE8);
+ return s;
+}
+
+/* unit = PIT frequency */
+static int64_t pit_get_time(PITChannel *pc)
+{
+ PITState *s = pc->pit_state;
+ return s->get_ticks(s->opaque);
+}
+
+static uint32_t pit_get_count(PITChannel *pc)
+{
+ uint32_t counter;
+ uint64_t d;
+
+ d = pit_get_time(pc) - pc->count_load_time;
+ switch(pc->mode) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ counter = (pc->count - d) & 0xffff;
+ break;
+ default:
+ counter = pc->count - (d % pc->count);
+ break;
+ }
+ return counter;
+}
+
+/* get pit output bit */
+static int pit_get_out(PITChannel *pc)
+{
+ int out;
+ int64_t d;
+
+ d = pit_get_time(pc) - pc->count_load_time;
+ switch(pc->mode) {
+ default:
+ case 0:
+ out = (d >= pc->count) >> 0;
+ break;
+ case 1:
+ out = (d < pc->count) >> 0;
+ break;
+ case 2:
+ /* mode used by Linux */
+ if ((d % pc->count) == 0 && d != 0)
+ out = 1;
+ else
+ out = 0;
+ break;
+ case 3:
+ out = ((d % pc->count) < (pc->count >> 1)) >> 0;
+ break;
+ case 4:
+ case 5:
+ out = (d == pc->count) >> 0;
+ break;
+ }
+ return out;
+}
+
+static void pit_load_count(PITChannel *s, int val)
+{
+ if (val == 0)
+ val = 0x10000;
+ s->count_load_time = pit_get_time(s);
+ s->last_irq_time = 0;
+ s->count = val;
+}
+
+static void pit_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ PITState *pit = opaque;
+ int channel, access, addr;
+ PITChannel *s;
+
+ addr = offset & 3;
+#ifdef DEBUG_PIT
+ printf("pit_write: off=%d val=0x%02x\n", addr, val);
+#endif
+ if (addr == 3) {
+ channel = val >> 6;
+ if (channel == 3)
+ return;
+ s = &pit->pit_channels[channel];
+ access = (val >> 4) & 3;
+ switch(access) {
+ case 0:
+ s->latched_count = pit_get_count(s);
+ s->rw_state = RW_STATE_LATCHED_WORD0;
+ break;
+ default:
+ s->mode = (val >> 1) & 7;
+ s->bcd = val & 1;
+ s->rw_state = access - 1 + RW_STATE_LSB;
+ break;
+ }
+ } else {
+ s = &pit->pit_channels[addr];
+ switch(s->rw_state) {
+ case RW_STATE_LSB:
+ pit_load_count(s, val);
+ break;
+ case RW_STATE_MSB:
+ pit_load_count(s, val << 8);
+ break;
+ case RW_STATE_WORD0:
+ case RW_STATE_WORD1:
+ if (s->rw_state & 1) {
+ pit_load_count(s, (s->latched_count & 0xff) | (val << 8));
+ } else {
+ s->latched_count = val;
+ }
+ s->rw_state ^= 1;
+ break;
+ }
+ }
+}
+
+static uint32_t pit_read(void *opaque, uint32_t offset, int size_log2)
+{
+ PITState *pit = opaque;
+ PITChannel *s;
+ int ret, count, addr;
+
+ addr = offset & 3;
+ if (addr == 3)
+ return 0xff;
+
+ s = &pit->pit_channels[addr];
+ switch(s->rw_state) {
+ case RW_STATE_LSB:
+ case RW_STATE_MSB:
+ case RW_STATE_WORD0:
+ case RW_STATE_WORD1:
+ count = pit_get_count(s);
+ if (s->rw_state & 1)
+ ret = (count >> 8) & 0xff;
+ else
+ ret = count & 0xff;
+ if (s->rw_state & 2)
+ s->rw_state ^= 1;
+ break;
+ default:
+ case RW_STATE_LATCHED_WORD0:
+ case RW_STATE_LATCHED_WORD1:
+ if (s->rw_state & 1)
+ ret = s->latched_count >> 8;
+ else
+ ret = s->latched_count & 0xff;
+ s->rw_state ^= 1;
+ break;
+ }
+#ifdef DEBUG_PIT
+ printf("pit_read: off=%d val=0x%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+static void speaker_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ PITState *pit = opaque;
+ pit->speaker_data_on = (val >> 1) & 1;
+ pit->pit_channels[2].gate = val & 1;
+}
+
+static uint32_t speaker_read(void *opaque, uint32_t offset, int size_log2)
+{
+ PITState *pit = opaque;
+ PITChannel *s;
+ int out, val;
+
+ s = &pit->pit_channels[2];
+ out = pit_get_out(s);
+ val = (pit->speaker_data_on << 1) | s->gate | (out << 5);
+#ifdef DEBUG_PIT
+ // console.log("speaker_read: addr=" + toHex2(addr) + " val=" + toHex2(val));
+#endif
+ return val;
+}
+
+/* set the IRQ if necessary and return the delay in ms until the next
+ IRQ. Note: The code does not handle all the PIT configurations. */
+static int pit_update_irq(PITState *pit)
+{
+ PITChannel *s;
+ int64_t d, delay;
+
+ s = &pit->pit_channels[0];
+
+ delay = PIT_FREQ; /* could be infinity delay */
+
+ d = pit_get_time(s) - s->count_load_time;
+ switch(s->mode) {
+ default:
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ if (s->last_irq_time == 0) {
+ delay = s->count - d;
+ if (delay <= 0) {
+ set_irq(pit->irq, 1);
+ set_irq(pit->irq, 0);
+ s->last_irq_time = d;
+ }
+ }
+ break;
+ case 2: /* mode used by Linux */
+ case 3:
+ delay = s->last_irq_time + s->count - d;
+ if (delay <= 0) {
+ set_irq(pit->irq, 1);
+ set_irq(pit->irq, 0);
+ s->last_irq_time += s->count;
+ }
+ break;
+ }
+
+ if (delay <= 0)
+ return 0;
+ else
+ return delay / (PIT_FREQ / 1000);
+}
+
+/***********************************************************/
+/* serial port emulation */
+
+#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
+
+#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
+#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
+#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
+#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
+
+#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
+#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
+
+#define UART_IIR_MSI 0x00 /* Modem status interrupt */
+#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
+#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
+#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
+#define UART_IIR_FE 0xC0 /* Fifo enabled */
+
+#define UART_LSR_TEMT 0x40 /* Transmitter empty */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_BI 0x10 /* Break interrupt indicator */
+#define UART_LSR_FE 0x08 /* Frame error indicator */
+#define UART_LSR_PE 0x04 /* Parity error indicator */
+#define UART_LSR_OE 0x02 /* Overrun error indicator */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+
+#define UART_FCR_XFR 0x04 /* XMIT Fifo Reset */
+#define UART_FCR_RFR 0x02 /* RCVR Fifo Reset */
+#define UART_FCR_FE 0x01 /* FIFO Enable */
+
+#define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */
+
+typedef struct {
+ uint8_t divider;
+ uint8_t rbr; /* receive register */
+ uint8_t ier;
+ uint8_t iir; /* read only */
+ uint8_t lcr;
+ uint8_t mcr;
+ uint8_t lsr; /* read only */
+ uint8_t msr;
+ uint8_t scr;
+ uint8_t fcr;
+ IRQSignal *irq;
+ void (*write_func)(void *opaque, const uint8_t *buf, int buf_len);
+ void *opaque;
+} SerialState;
+
+static void serial_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2);
+static uint32_t serial_read(void *opaque, uint32_t offset, int size_log2);
+
+SerialState *serial_init(PhysMemoryMap *port_map, int addr,
+ IRQSignal *irq,
+ void (*write_func)(void *opaque, const uint8_t *buf, int buf_len), void *opaque)
+{
+ SerialState *s;
+ s = mallocz(sizeof(*s));
+
+ /* all 8 bit registers */
+ s->divider = 0;
+ s->rbr = 0; /* receive register */
+ s->ier = 0;
+ s->iir = UART_IIR_NO_INT; /* read only */
+ s->lcr = 0;
+ s->mcr = 0;
+ s->lsr = UART_LSR_TEMT | UART_LSR_THRE; /* read only */
+ s->msr = 0;
+ s->scr = 0;
+ s->fcr = 0;
+
+ s->irq = irq;
+ s->write_func = write_func;
+ s->opaque = opaque;
+
+ cpu_register_device(port_map, addr, 8, s, serial_read, serial_write,
+ DEVIO_SIZE8);
+ return s;
+}
+
+static void serial_update_irq(SerialState *s)
+{
+ if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) {
+ s->iir = UART_IIR_RDI;
+ } else if ((s->lsr & UART_LSR_THRE) && (s->ier & UART_IER_THRI)) {
+ s->iir = UART_IIR_THRI;
+ } else {
+ s->iir = UART_IIR_NO_INT;
+ }
+ if (s->iir != UART_IIR_NO_INT) {
+ set_irq(s->irq, 1);
+ } else {
+ set_irq(s->irq, 0);
+ }
+}
+
+#if 0
+/* send remainining chars in fifo */
+Serial.prototype.write_tx_fifo = function()
+{
+ if (s->tx_fifo != "") {
+ s->write_func(s->tx_fifo);
+ s->tx_fifo = "";
+
+ s->lsr |= UART_LSR_THRE;
+ s->lsr |= UART_LSR_TEMT;
+ s->update_irq();
+ }
+}
+#endif
+
+static void serial_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+ SerialState *s = opaque;
+ int addr;
+
+ addr = offset & 7;
+ switch(addr) {
+ default:
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ s->divider = (s->divider & 0xff00) | val;
+ } else {
+#if 0
+ if (s->fcr & UART_FCR_FE) {
+ s->tx_fifo += String.fromCharCode(val);
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+ if (s->tx_fifo.length >= UART_FIFO_LENGTH) {
+ /* write to the terminal */
+ s->write_tx_fifo();
+ }
+ } else
+#endif
+ {
+ uint8_t ch;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+
+ /* write to the terminal */
+ ch = val;
+ s->write_func(s->opaque, &ch, 1);
+ s->lsr |= UART_LSR_THRE;
+ s->lsr |= UART_LSR_TEMT;
+ serial_update_irq(s);
+ }
+ }
+ break;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ s->divider = (s->divider & 0x00ff) | (val << 8);
+ } else {
+ s->ier = val;
+ serial_update_irq(s);
+ }
+ break;
+ case 2:
+#if 0
+ if ((s->fcr ^ val) & UART_FCR_FE) {
+ /* clear fifos */
+ val |= UART_FCR_XFR | UART_FCR_RFR;
+ }
+ if (val & UART_FCR_XFR)
+ s->tx_fifo = "";
+ if (val & UART_FCR_RFR)
+ s->rx_fifo = "";
+ s->fcr = val & UART_FCR_FE;
+#endif
+ break;
+ case 3:
+ s->lcr = val;
+ break;
+ case 4:
+ s->mcr = val;
+ break;
+ case 5:
+ break;
+ case 6:
+ s->msr = val;
+ break;
+ case 7:
+ s->scr = val;
+ break;
+ }
+}
+
+static uint32_t serial_read(void *opaque, uint32_t offset, int size_log2)
+{
+ SerialState *s = opaque;
+ int ret, addr;
+
+ addr = offset & 7;
+ switch(addr) {
+ default:
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ ret = s->divider & 0xff;
+ } else {
+ ret = s->rbr;
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ serial_update_irq(s);
+#if 0
+ /* try to receive next chars */
+ s->send_char_from_fifo();
+#endif
+ }
+ break;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ ret = (s->divider >> 8) & 0xff;
+ } else {
+ ret = s->ier;
+ }
+ break;
+ case 2:
+ ret = s->iir;
+ if (s->fcr & UART_FCR_FE)
+ ret |= UART_IIR_FE;
+ break;
+ case 3:
+ ret = s->lcr;
+ break;
+ case 4:
+ ret = s->mcr;
+ break;
+ case 5:
+ ret = s->lsr;
+ break;
+ case 6:
+ ret = s->msr;
+ break;
+ case 7:
+ ret = s->scr;
+ break;
+ }
+ return ret;
+}
+
+void serial_send_break(SerialState *s)
+{
+ s->rbr = 0;
+ s->lsr |= UART_LSR_BI | UART_LSR_DR;
+ serial_update_irq(s);
+}
+
+#if 0
+static void serial_send_char(SerialState *s, int ch)
+{
+ s->rbr = ch;
+ s->lsr |= UART_LSR_DR;
+ serial_update_irq(s);
+}
+
+Serial.prototype.send_char_from_fifo = function()
+{
+ var fifo;
+
+ fifo = s->rx_fifo;
+ if (fifo != "" && !(s->lsr & UART_LSR_DR)) {
+ s->send_char(fifo.charCodeAt(0));
+ s->rx_fifo = fifo.substr(1, fifo.length - 1);
+ }
+}
+
+/* queue the string in the UART receive fifo and send it ASAP */
+Serial.prototype.send_chars = function(str)
+{
+ s->rx_fifo += str;
+ s->send_char_from_fifo();
+}
+
+#endif
+
+#ifdef DEBUG_BIOS
+static void bios_debug_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+#ifdef EMSCRIPTEN
+ static char line_buf[256];
+ static int line_buf_index;
+ line_buf[line_buf_index++] = val;
+ if (val == '\n' || line_buf_index >= sizeof(line_buf) - 1) {
+ line_buf[line_buf_index] = '\0';
+ printf("%s", line_buf);
+ line_buf_index = 0;
+ }
+#else
+ putchar(val & 0xff);
+#endif
+}
+
+static uint32_t bios_debug_read(void *opaque, uint32_t offset, int size_log2)
+{
+ return 0;
+}
+#endif
+
+typedef struct PCMachine {
+ VirtMachine common;
+ uint64_t ram_size;
+ PhysMemoryMap *mem_map;
+ PhysMemoryMap *port_map;
+
+ X86CPUState *cpu_state;
+ PIC2State *pic_state;
+ IRQSignal pic_irq[16];
+ PITState *pit_state;
+ I440FXState *i440fx_state;
+ CMOSState *cmos_state;
+ SerialState *serial_state;
+
+ /* input */
+ VIRTIODevice *keyboard_dev;
+ VIRTIODevice *mouse_dev;
+ KBDState *kbd_state;
+ PS2MouseState *ps2_mouse;
+ VMMouseState *vm_mouse;
+ PS2KbdState *ps2_kbd;
+
+#ifdef USE_KVM
+ BOOL kvm_enabled;
+ int kvm_fd;
+ int vm_fd;
+ int vcpu_fd;
+ int kvm_run_size;
+ struct kvm_run *kvm_run;
+#endif
+} PCMachine;
+
+static void copy_kernel(PCMachine *s, const uint8_t *buf, int buf_len,
+ const char *cmd_line);
+
+static void port80_write(void *opaque, uint32_t offset,
+ uint32_t val64, int size_log2)
+{
+}
+
+static uint32_t port80_read(void *opaque, uint32_t offset, int size_log2)
+{
+ return 0xff;
+}
+
+static void port92_write(void *opaque, uint32_t offset,
+ uint32_t val, int size_log2)
+{
+}
+
+static uint32_t port92_read(void *opaque, uint32_t offset, int size_log2)
+{
+ int a20 = 1; /* A20=0 is not supported */
+ return a20 << 1;
+}
+
+#define VMPORT_MAGIC 0x564D5868
+#define REG_EAX 0
+#define REG_EBX 1
+#define REG_ECX 2
+#define REG_EDX 3
+#define REG_ESI 4
+#define REG_EDI 5
+
+static uint32_t vmport_read(void *opaque, uint32_t addr, int size_log2)
+{
+ PCMachine *s = opaque;
+ uint32_t regs[6];
+
+#ifdef USE_KVM
+ if (s->kvm_enabled) {
+ struct kvm_regs r;
+
+ ioctl(s->vcpu_fd, KVM_GET_REGS, &r);
+ regs[REG_EAX] = r.rax;
+ regs[REG_EBX] = r.rbx;
+ regs[REG_ECX] = r.rcx;
+ regs[REG_EDX] = r.rdx;
+ regs[REG_ESI] = r.rsi;
+ regs[REG_EDI] = r.rdi;
+
+ if (regs[REG_EAX] == VMPORT_MAGIC) {
+
+ vmmouse_handler(s->vm_mouse, regs);
+
+ /* Note: in 64 bits the high parts are reset to zero
+ in all cases. */
+ r.rax = regs[REG_EAX];
+ r.rbx = regs[REG_EBX];
+ r.rcx = regs[REG_ECX];
+ r.rdx = regs[REG_EDX];
+ r.rsi = regs[REG_ESI];
+ r.rdi = regs[REG_EDI];
+ ioctl(s->vcpu_fd, KVM_SET_REGS, &r);
+ }
+ } else
+#endif
+ {
+ regs[REG_EAX] = x86_cpu_get_reg(s->cpu_state, 0);
+ regs[REG_EBX] = x86_cpu_get_reg(s->cpu_state, 3);
+ regs[REG_ECX] = x86_cpu_get_reg(s->cpu_state, 1);
+ regs[REG_EDX] = x86_cpu_get_reg(s->cpu_state, 2);
+ regs[REG_ESI] = x86_cpu_get_reg(s->cpu_state, 6);
+ regs[REG_EDI] = x86_cpu_get_reg(s->cpu_state, 7);
+
+ if (regs[REG_EAX] == VMPORT_MAGIC) {
+ vmmouse_handler(s->vm_mouse, regs);
+
+ x86_cpu_set_reg(s->cpu_state, 0, regs[REG_EAX]);
+ x86_cpu_set_reg(s->cpu_state, 3, regs[REG_EBX]);
+ x86_cpu_set_reg(s->cpu_state, 1, regs[REG_ECX]);
+ x86_cpu_set_reg(s->cpu_state, 2, regs[REG_EDX]);
+ x86_cpu_set_reg(s->cpu_state, 6, regs[REG_ESI]);
+ x86_cpu_set_reg(s->cpu_state, 7, regs[REG_EDI]);
+ }
+ }
+ return regs[REG_EAX];
+}
+
+static void vmport_write(void *opaque, uint32_t addr, uint32_t val,
+ int size_log2)
+{
+}
+
+static void pic_set_irq_cb(void *opaque, int level)
+{
+ PCMachine *s = opaque;
+ x86_cpu_set_irq(s->cpu_state, level);
+}
+
+static void serial_write_cb(void *opaque, const uint8_t *buf, int buf_len)
+{
+ PCMachine *s = opaque;
+ if (s->common.console) {
+ s->common.console->write_data(s->common.console->opaque, buf, buf_len);
+ }
+}
+
+static int get_hard_intno_cb(void *opaque)
+{
+ PCMachine *s = opaque;
+ return pic2_get_hard_intno(s->pic_state);
+}
+
+static int64_t pit_get_ticks_cb(void *opaque)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint64_t)ts.tv_sec * PIT_FREQ +
+ ((uint64_t)ts.tv_nsec * PIT_FREQ / 1000000000);
+}
+
+#define FRAMEBUFFER_BASE_ADDR 0xf0400000
+
+static uint8_t *get_ram_ptr(PCMachine *s, uint64_t paddr)
+{
+ PhysMemoryRange *pr;
+ pr = get_phys_mem_range(s->mem_map, paddr);
+ if (!pr || !pr->is_ram)
+ return NULL;
+ return pr->phys_mem + (uintptr_t)(paddr - pr->addr);
+}
+
+#ifdef DUMP_IOPORT
+static BOOL dump_port(int port)
+{
+ return !((port >= 0x1f0 && port <= 0x1f7) ||
+ (port >= 0x20 && port <= 0x21) ||
+ (port >= 0xa0 && port <= 0xa1));
+}
+#endif
+
+static void st_port(void *opaque, uint32_t port, uint32_t val, int size_log2)
+{
+ PCMachine *s = opaque;
+ PhysMemoryRange *pr;
+#ifdef DUMP_IOPORT
+ if (dump_port(port))
+ printf("write port=0x%x val=0x%x s=%d\n", port, val, 1 << size_log2);
+#endif
+ pr = get_phys_mem_range(s->port_map, port);
+ if (!pr) {
+ return;
+ }
+ port -= pr->addr;
+ if ((pr->devio_flags >> size_log2) & 1) {
+ pr->write_func(pr->opaque, port, (uint32_t)val, size_log2);
+ } else if (size_log2 == 1 && (pr->devio_flags & DEVIO_SIZE8)) {
+ pr->write_func(pr->opaque, port, val & 0xff, 0);
+ pr->write_func(pr->opaque, port + 1, (val >> 8) & 0xff, 0);
+ }
+}
+
+static uint32_t ld_port(void *opaque, uint32_t port1, int size_log2)
+{
+ PCMachine *s = opaque;
+ PhysMemoryRange *pr;
+ uint32_t val, port;
+
+ port = port1;
+ pr = get_phys_mem_range(s->port_map, port);
+ if (!pr) {
+ val = -1;
+ } else {
+ port -= pr->addr;
+ if ((pr->devio_flags >> size_log2) & 1) {
+ val = pr->read_func(pr->opaque, port, size_log2);
+ } else if (size_log2 == 1 && (pr->devio_flags & DEVIO_SIZE8)) {
+ val = pr->read_func(pr->opaque, port, 0) & 0xff;
+ val |= (pr->read_func(pr->opaque, port + 1, 0) & 0xff) << 8;
+ } else {
+ val = -1;
+ }
+ }
+#ifdef DUMP_IOPORT
+ if (dump_port(port1))
+ printf("read port=0x%x val=0x%x s=%d\n", port1, val, 1 << size_log2);
+#endif
+ return val;
+}
+
+static void pc_machine_set_defaults(VirtMachineParams *p)
+{
+ p->accel_enable = TRUE;
+}
+
+#ifdef USE_KVM
+
+static void sigalrm_handler(int sig)
+{
+}
+
+#define CPUID_APIC (1 << 9)
+#define CPUID_ACPI (1 << 22)
+
+static void kvm_set_cpuid(PCMachine *s)
+{
+ struct kvm_cpuid2 *kvm_cpuid;
+ int n_ent_max, i;
+ struct kvm_cpuid_entry2 *ent;
+
+ n_ent_max = 128;
+ kvm_cpuid = mallocz(sizeof(struct kvm_cpuid2) + n_ent_max * sizeof(kvm_cpuid->entries[0]));
+
+ kvm_cpuid->nent = n_ent_max;
+ if (ioctl(s->kvm_fd, KVM_GET_SUPPORTED_CPUID, kvm_cpuid) < 0) {
+ perror("KVM_GET_SUPPORTED_CPUID");
+ exit(1);
+ }
+
+ for(i = 0; i < kvm_cpuid->nent; i++) {
+ ent = &kvm_cpuid->entries[i];
+ /* remove the APIC & ACPI to be in sync with the emulator */
+ if (ent->function == 1 || ent->function == 0x80000001) {
+ ent->edx &= ~(CPUID_APIC | CPUID_ACPI);
+ }
+ }
+
+ if (ioctl(s->vcpu_fd, KVM_SET_CPUID2, kvm_cpuid) < 0) {
+ perror("KVM_SET_CPUID2");
+ exit(1);
+ }
+ free(kvm_cpuid);
+}
+
+/* XXX: should check overlapping mappings */
+static void kvm_map_ram(PhysMemoryMap *mem_map, PhysMemoryRange *pr)
+{
+ PCMachine *s = mem_map->opaque;
+ struct kvm_userspace_memory_region region;
+ int flags;
+
+ region.slot = pr - mem_map->phys_mem_range;
+ flags = 0;
+ if (pr->devram_flags & DEVRAM_FLAG_ROM)
+ flags |= KVM_MEM_READONLY;
+ if (pr->devram_flags & DEVRAM_FLAG_DIRTY_BITS)
+ flags |= KVM_MEM_LOG_DIRTY_PAGES;
+ region.flags = flags;
+ region.guest_phys_addr = pr->addr;
+ region.memory_size = pr->size;
+#if 0
+ printf("map slot %d: %08lx %08lx\n",
+ region.slot, pr->addr, pr->size);
+#endif
+ region.userspace_addr = (uintptr_t)pr->phys_mem;
+ if (ioctl(s->vm_fd, KVM_SET_USER_MEMORY_REGION, &region) < 0) {
+ perror("KVM_SET_USER_MEMORY_REGION");
+ exit(1);
+ }
+}
+
+/* XXX: just for one region */
+static PhysMemoryRange *kvm_register_ram(PhysMemoryMap *mem_map, uint64_t addr,
+ uint64_t size, int devram_flags)
+{
+ PhysMemoryRange *pr;
+ uint8_t *phys_mem;
+
+ pr = register_ram_entry(mem_map, addr, size, devram_flags);
+
+ phys_mem = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (!phys_mem)
+ return NULL;
+ pr->phys_mem = phys_mem;
+ if (devram_flags & DEVRAM_FLAG_DIRTY_BITS) {
+ int n_pages = size >> 12;
+ pr->dirty_bits_size = ((n_pages + 63) / 64) * 8;
+ pr->dirty_bits = mallocz(pr->dirty_bits_size);
+ }
+
+ if (pr->size != 0) {
+ kvm_map_ram(mem_map, pr);
+ }
+ return pr;
+}
+
+static void kvm_set_ram_addr(PhysMemoryMap *mem_map,
+ PhysMemoryRange *pr, uint64_t addr, BOOL enabled)
+{
+ if (enabled) {
+ if (pr->size == 0 || addr != pr->addr) {
+ /* move or create the region */
+ pr->size = pr->org_size;
+ pr->addr = addr;
+ kvm_map_ram(mem_map, pr);
+ }
+ } else {
+ if (pr->size != 0) {
+ pr->addr = 0;
+ pr->size = 0;
+ /* map a zero size region to disable */
+ kvm_map_ram(mem_map, pr);
+ }
+ }
+}
+
+static const uint32_t *kvm_get_dirty_bits(PhysMemoryMap *mem_map,
+ PhysMemoryRange *pr)
+{
+ PCMachine *s = mem_map->opaque;
+ struct kvm_dirty_log dlog;
+
+ if (pr->size == 0) {
+ /* not mapped: we assume no modification was made */
+ memset(pr->dirty_bits, 0, pr->dirty_bits_size);
+ } else {
+ dlog.slot = pr - mem_map->phys_mem_range;
+ dlog.dirty_bitmap = pr->dirty_bits;
+ if (ioctl(s->vm_fd, KVM_GET_DIRTY_LOG, &dlog) < 0) {
+ perror("KVM_GET_DIRTY_LOG");
+ exit(1);
+ }
+ }
+ return pr->dirty_bits;
+}
+
+static void kvm_free_ram(PhysMemoryMap *mem_map, PhysMemoryRange *pr)
+{
+ /* XXX: do it */
+ munmap(pr->phys_mem, pr->org_size);
+ free(pr->dirty_bits);
+}
+
+static void kvm_pic_set_irq(void *opaque, int irq_num, int level)
+{
+ PCMachine *s = opaque;
+ struct kvm_irq_level irq_level;
+ irq_level.irq = irq_num;
+ irq_level.level = level;
+ if (ioctl(s->vm_fd, KVM_IRQ_LINE, &irq_level) < 0) {
+ perror("KVM_IRQ_LINE");
+ exit(1);
+ }
+}
+
+static void kvm_init(PCMachine *s)
+{
+ int ret, i;
+ struct sigaction act;
+ struct kvm_pit_config pit_config;
+ uint64_t base_addr;
+
+ s->kvm_enabled = FALSE;
+ s->kvm_fd = open("/dev/kvm", O_RDWR);
+ if (s->kvm_fd < 0) {
+ fprintf(stderr, "KVM not available\n");
+ return;
+ }
+ ret = ioctl(s->kvm_fd, KVM_GET_API_VERSION, 0);
+ if (ret < 0) {
+ perror("KVM_GET_API_VERSION");
+ exit(1);
+ }
+ if (ret != 12) {
+ fprintf(stderr, "Unsupported KVM version\n");
+ close(s->kvm_fd);
+ s->kvm_fd = -1;
+ return;
+ }
+ s->vm_fd = ioctl(s->kvm_fd, KVM_CREATE_VM, 0);
+ if (s->vm_fd < 0) {
+ perror("KVM_CREATE_VM");
+ exit(1);
+ }
+
+ /* just before the BIOS */
+ base_addr = 0xfffbc000;
+ if (ioctl(s->vm_fd, KVM_SET_IDENTITY_MAP_ADDR, &base_addr) < 0) {
+ perror("KVM_SET_IDENTITY_MAP_ADDR");
+ exit(1);
+ }
+
+ if (ioctl(s->vm_fd, KVM_SET_TSS_ADDR, (long)(base_addr + 0x1000)) < 0) {
+ perror("KVM_SET_TSS_ADDR");
+ exit(1);
+ }
+
+ if (ioctl(s->vm_fd, KVM_CREATE_IRQCHIP, 0) < 0) {
+ perror("KVM_CREATE_IRQCHIP");
+ exit(1);
+ }
+
+ memset(&pit_config, 0, sizeof(pit_config));
+ pit_config.flags = KVM_PIT_SPEAKER_DUMMY;
+ if (ioctl(s->vm_fd, KVM_CREATE_PIT2, &pit_config)) {
+ perror("KVM_CREATE_PIT2");
+ exit(1);
+ }
+
+ s->vcpu_fd = ioctl(s->vm_fd, KVM_CREATE_VCPU, 0);
+ if (s->vcpu_fd < 0) {
+ perror("KVM_CREATE_VCPU");
+ exit(1);
+ }
+
+ kvm_set_cpuid(s);
+
+ /* map the kvm_run structure */
+ s->kvm_run_size = ioctl(s->kvm_fd, KVM_GET_VCPU_MMAP_SIZE, NULL);
+ if (s->kvm_run_size < 0) {
+ perror("KVM_GET_VCPU_MMAP_SIZE");
+ exit(1);
+ }
+
+ s->kvm_run = mmap(NULL, s->kvm_run_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, s->vcpu_fd, 0);
+ if (!s->kvm_run) {
+ perror("mmap kvm_run");
+ exit(1);
+ }
+
+ for(i = 0; i < 16; i++) {
+ irq_init(&s->pic_irq[i], kvm_pic_set_irq, s, i);
+ }
+
+ act.sa_handler = sigalrm_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGALRM, &act, NULL);
+
+ s->kvm_enabled = TRUE;
+
+ s->mem_map->register_ram = kvm_register_ram;
+ s->mem_map->free_ram = kvm_free_ram;
+ s->mem_map->get_dirty_bits = kvm_get_dirty_bits;
+ s->mem_map->set_ram_addr = kvm_set_ram_addr;
+ s->mem_map->opaque = s;
+}
+
+static void kvm_exit_io(PCMachine *s, struct kvm_run *run)
+{
+ uint8_t *ptr;
+ int i;
+
+ ptr = (uint8_t *)run + run->io.data_offset;
+ // printf("port: addr=%04x\n", run->io.port);
+
+ for(i = 0; i < run->io.count; i++) {
+ if (run->io.direction == KVM_EXIT_IO_OUT) {
+ switch(run->io.size) {
+ case 1:
+ st_port(s, run->io.port, *(uint8_t *)ptr, 0);
+ break;
+ case 2:
+ st_port(s, run->io.port, *(uint16_t *)ptr, 1);
+ break;
+ case 4:
+ st_port(s, run->io.port, *(uint32_t *)ptr, 2);
+ break;
+ default:
+ abort();
+ }
+ } else {
+ switch(run->io.size) {
+ case 1:
+ *(uint8_t *)ptr = ld_port(s, run->io.port, 0);
+ break;
+ case 2:
+ *(uint16_t *)ptr = ld_port(s, run->io.port, 1);
+ break;
+ case 4:
+ *(uint32_t *)ptr = ld_port(s, run->io.port, 2);
+ break;
+ default:
+ abort();
+ }
+ }
+ ptr += run->io.size;
+ }
+}
+
+static void kvm_exit_mmio(PCMachine *s, struct kvm_run *run)
+{
+ uint8_t *data = run->mmio.data;
+ PhysMemoryRange *pr;
+ uint64_t addr;
+
+ pr = get_phys_mem_range(s->mem_map, run->mmio.phys_addr);
+ if (run->mmio.is_write) {
+ if (!pr || pr->is_ram)
+ return;
+ addr = run->mmio.phys_addr - pr->addr;
+ switch(run->mmio.len) {
+ case 1:
+ if (pr->devio_flags & DEVIO_SIZE8) {
+ pr->write_func(pr->opaque, addr, *(uint8_t *)data, 0);
+ }
+ break;
+ case 2:
+ if (pr->devio_flags & DEVIO_SIZE16) {
+ pr->write_func(pr->opaque, addr, *(uint16_t *)data, 1);
+ }
+ break;
+ case 4:
+ if (pr->devio_flags & DEVIO_SIZE32) {
+ pr->write_func(pr->opaque, addr, *(uint32_t *)data, 2);
+ }
+ break;
+ case 8:
+ if (pr->devio_flags & DEVIO_SIZE32) {
+ pr->write_func(pr->opaque, addr, *(uint32_t *)data, 2);
+ pr->write_func(pr->opaque, addr + 4, *(uint32_t *)(data + 4), 2);
+ }
+ break;
+ default:
+ abort();
+ }
+ } else {
+ if (!pr || pr->is_ram)
+ goto no_dev;
+ addr = run->mmio.phys_addr - pr->addr;
+ switch(run->mmio.len) {
+ case 1:
+ if (!(pr->devio_flags & DEVIO_SIZE8))
+ goto no_dev;
+ *(uint8_t *)data = pr->read_func(pr->opaque, addr, 0);
+ break;
+ case 2:
+ if (!(pr->devio_flags & DEVIO_SIZE16))
+ goto no_dev;
+ *(uint16_t *)data = pr->read_func(pr->opaque, addr, 1);
+ break;
+ case 4:
+ if (!(pr->devio_flags & DEVIO_SIZE32))
+ goto no_dev;
+ *(uint32_t *)data = pr->read_func(pr->opaque, addr, 2);
+ break;
+ case 8:
+ if (pr->devio_flags & DEVIO_SIZE32) {
+ *(uint32_t *)data =
+ pr->read_func(pr->opaque, addr, 2);
+ *(uint32_t *)(data + 4) =
+ pr->read_func(pr->opaque, addr + 4, 2);
+ } else {
+ no_dev:
+ memset(run->mmio.data, 0, run->mmio.len);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ }
+}
+
+static void kvm_exec(PCMachine *s)
+{
+ struct kvm_run *run = s->kvm_run;
+ struct itimerval ival;
+ int ret;
+
+ /* Not efficient but simple: we use a timer to interrupt the
+ execution after a given time */
+ ival.it_interval.tv_sec = 0;
+ ival.it_interval.tv_usec = 0;
+ ival.it_value.tv_sec = 0;
+ ival.it_value.tv_usec = 10 * 1000; /* 10 ms max */
+ setitimer(ITIMER_REAL, &ival, NULL);
+
+ ret = ioctl(s->vcpu_fd, KVM_RUN, 0);
+ if (ret < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ /* timeout */
+ return;
+ }
+ perror("KVM_RUN");
+ exit(1);
+ }
+ // printf("exit=%d\n", run->exit_reason);
+ switch(run->exit_reason) {
+ case KVM_EXIT_HLT:
+ break;
+ case KVM_EXIT_IO:
+ kvm_exit_io(s, run);
+ break;
+ case KVM_EXIT_MMIO:
+ kvm_exit_mmio(s, run);
+ break;
+ case KVM_EXIT_FAIL_ENTRY:
+ fprintf(stderr, "KVM_EXIT_FAIL_ENTRY: reason=0x%" PRIx64 "\n",
+ (uint64_t)run->fail_entry.hardware_entry_failure_reason);
+#if 0
+ {
+ struct kvm_regs regs;
+ if (ioctl(s->vcpu_fd, KVM_GET_REGS, &regs) < 0) {
+ perror("KVM_SET_REGS");
+ exit(1);
+ }
+ printf("RIP=%016" PRIx64 "\n", (uint64_t)regs.rip);
+ }
+#endif
+ exit(1);
+ case KVM_EXIT_INTERNAL_ERROR:
+ fprintf(stderr, "KVM_EXIT_INTERNAL_ERROR: suberror=0x%x\n",
+ (uint32_t)run->internal.suberror);
+ exit(1);
+ default:
+ fprintf(stderr, "KVM: unsupported exit_reason=%d\n", run->exit_reason);
+ exit(1);
+ }
+}
+#endif
+
+#if defined(EMSCRIPTEN)
+/* with Javascript clock_gettime() is not enough precise enough to
+ have a reliable TSC counter. XXX: increment the cycles during the
+ power down time */
+static uint64_t cpu_get_tsc(void *opaque)
+{
+ PCMachine *s = opaque;
+ uint64_t c;
+ c = x86_cpu_get_cycles(s->cpu_state);
+ return c;
+}
+#else
+
+#define TSC_FREQ 100000000
+
+static uint64_t cpu_get_tsc(void *opaque)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint64_t)ts.tv_sec * TSC_FREQ +
+ (ts.tv_nsec / (1000000000 / TSC_FREQ));
+}
+#endif
+
+static void pc_flush_tlb_write_range(void *opaque, uint8_t *ram_addr,
+ size_t ram_size)
+{
+ PCMachine *s = opaque;
+ x86_cpu_flush_tlb_write_range_ram(s->cpu_state, ram_addr, ram_size);
+}
+
+static VirtMachine *pc_machine_init(const VirtMachineParams *p)
+{
+ PCMachine *s;
+ int i, piix3_devfn;
+ PCIBus *pci_bus;
+ VIRTIOBusDef vbus_s, *vbus = &vbus_s;
+
+ if (strcmp(p->machine_name, "pc") != 0) {
+ vm_error("unsupported machine: %s\n", p->machine_name);
+ return NULL;
+ }
+
+ assert(p->ram_size >= (1 << 20));
+
+ s = mallocz(sizeof(*s));
+ s->common.vmc = p->vmc;
+ s->ram_size = p->ram_size;
+
+ s->port_map = phys_mem_map_init();
+ s->mem_map = phys_mem_map_init();
+
+#ifdef USE_KVM
+ if (p->accel_enable) {
+ kvm_init(s);
+ }
+#endif
+
+#ifdef USE_KVM
+ if (!s->kvm_enabled)
+#endif
+ {
+ s->cpu_state = x86_cpu_init(s->mem_map);
+ x86_cpu_set_get_tsc(s->cpu_state, cpu_get_tsc, s);
+ x86_cpu_set_port_io(s->cpu_state, ld_port, st_port, s);
+
+ /* needed to handle the RAM dirty bits */
+ s->mem_map->opaque = s;
+ s->mem_map->flush_tlb_write_range = pc_flush_tlb_write_range;
+ }
+
+ /* set the RAM mapping and leave the VGA addresses empty */
+ cpu_register_ram(s->mem_map, 0xc0000, p->ram_size - 0xc0000, 0);
+ cpu_register_ram(s->mem_map, 0, 0xa0000, 0);
+
+ /* devices */
+ cpu_register_device(s->port_map, 0x80, 2, s, port80_read, port80_write,
+ DEVIO_SIZE8);
+ cpu_register_device(s->port_map, 0x92, 2, s, port92_read, port92_write,
+ DEVIO_SIZE8);
+
+ /* setup the bios */
+ if (p->files[VM_FILE_BIOS].len > 0) {
+ int bios_size, bios_size1;
+ uint8_t *bios_buf, *ptr;
+ uint32_t bios_addr;
+
+ bios_size = p->files[VM_FILE_BIOS].len;
+ bios_buf = p->files[VM_FILE_BIOS].buf;
+ assert((bios_size % 65536) == 0 && bios_size != 0);
+ bios_addr = -bios_size;
+ /* at the top of the 4GB memory */
+ cpu_register_ram(s->mem_map, bios_addr, bios_size, DEVRAM_FLAG_ROM);
+ ptr = get_ram_ptr(s, bios_addr);
+ memcpy(ptr, bios_buf, bios_size);
+ /* in the lower 1MB memory (currently set as RAM) */
+ bios_size1 = min_int(bios_size, 128 * 1024);
+ ptr = get_ram_ptr(s, 0x100000 - bios_size1);
+ memcpy(ptr, bios_buf + bios_size - bios_size1, bios_size1);
+#ifdef DEBUG_BIOS
+ cpu_register_device(s->port_map, 0x402, 2, s,
+ bios_debug_read, bios_debug_write,
+ DEVIO_SIZE8);
+#endif
+ }
+
+#ifdef USE_KVM
+ if (!s->kvm_enabled)
+#endif
+ {
+ s->pic_state = pic2_init(s->port_map, 0x20, 0xa0,
+ 0x4d0, 0x4d1,
+ pic_set_irq_cb, s,
+ s->pic_irq);
+ x86_cpu_set_get_hard_intno(s->cpu_state, get_hard_intno_cb, s);
+ s->pit_state = pit_init(s->port_map, 0x40, 0x61, &s->pic_irq[0],
+ pit_get_ticks_cb, s);
+ }
+
+ s->cmos_state = cmos_init(s->port_map, 0x70, &s->pic_irq[8],
+ p->rtc_local_time);
+
+ /* various cmos data */
+ {
+ int size;
+ /* memory size */
+ size = min_int((s->ram_size - (1 << 20)) >> 10, 65535);
+ put_le16(s->cmos_state->cmos_data + 0x30, size);
+ if (s->ram_size >= (16 << 20)) {
+ size = min_int((s->ram_size - (16 << 20)) >> 16, 65535);
+ put_le16(s->cmos_state->cmos_data + 0x34, size);
+ }
+ s->cmos_state->cmos_data[0x14] = 0x06; /* mouse + FPU present */
+ }
+
+ s->i440fx_state = i440fx_init(&pci_bus, &piix3_devfn, s->mem_map,
+ s->port_map, s->pic_irq);
+
+ s->common.console = p->console;
+ /* serial console */
+ if (0) {
+ s->serial_state = serial_init(s->port_map, 0x3f8, &s->pic_irq[4],
+ serial_write_cb, s);
+ }
+
+ memset(vbus, 0, sizeof(*vbus));
+ vbus->pci_bus = pci_bus;
+
+ if (p->console) {
+ /* virtio console */
+ s->common.console_dev = virtio_console_init(vbus, p->console);
+ }
+
+ /* block devices */
+ for(i = 0; i < p->drive_count;) {
+ const VMDriveEntry *de = &p->tab_drive[i];
+
+ if (!de->device || !strcmp(de->device, "virtio")) {
+ virtio_block_init(vbus, p->tab_drive[i].block_dev);
+ i++;
+ } else if (!strcmp(de->device, "ide")) {
+ BlockDevice *tab_bs[2];
+
+ tab_bs[0] = p->tab_drive[i++].block_dev;
+ tab_bs[1] = NULL;
+ if (i < p->drive_count)
+ tab_bs[1] = p->tab_drive[i++].block_dev;
+ ide_init(s->port_map, 0x1f0, 0x3f6, &s->pic_irq[14], tab_bs);
+ piix3_ide_init(pci_bus, piix3_devfn + 1);
+ }
+ }
+
+ /* virtio filesystem */
+ for(i = 0; i < p->fs_count; i++) {
+ virtio_9p_init(vbus, p->tab_fs[i].fs_dev,
+ p->tab_fs[i].tag);
+ }
+
+ if (p->display_device) {
+ FBDevice *fb_dev;
+
+ fb_dev = mallocz(sizeof(*fb_dev));
+ s->common.fb_dev = fb_dev;
+ if (!strcmp(p->display_device, "vga")) {
+ int bios_size;
+ uint8_t *bios_buf;
+ bios_size = p->files[VM_FILE_VGA_BIOS].len;
+ bios_buf = p->files[VM_FILE_VGA_BIOS].buf;
+ pci_vga_init(pci_bus, fb_dev, p->width, p->height,
+ bios_buf, bios_size);
+ } else if (!strcmp(p->display_device, "simplefb")) {
+ simplefb_init(s->mem_map,
+ FRAMEBUFFER_BASE_ADDR,
+ fb_dev, p->width, p->height);
+ } else {
+ vm_error("unsupported display device: %s\n", p->display_device);
+ exit(1);
+ }
+ }
+
+ if (p->input_device) {
+ if (!strcmp(p->input_device, "virtio")) {
+ s->keyboard_dev = virtio_input_init(vbus, VIRTIO_INPUT_TYPE_KEYBOARD);
+
+ s->mouse_dev = virtio_input_init(vbus, VIRTIO_INPUT_TYPE_TABLET);
+ } else if (!strcmp(p->input_device, "ps2")) {
+ s->kbd_state = i8042_init(&s->ps2_kbd, &s->ps2_mouse,
+ s->port_map,
+ &s->pic_irq[1], &s->pic_irq[12], 0x60);
+ /* vmmouse */
+ cpu_register_device(s->port_map, 0x5658, 1, s,
+ vmport_read, vmport_write,
+ DEVIO_SIZE32);
+ s->vm_mouse = vmmouse_init(s->ps2_mouse);
+ } else {
+ vm_error("unsupported input device: %s\n", p->input_device);
+ exit(1);
+ }
+ }
+
+ /* virtio net device */
+ for(i = 0; i < p->eth_count; i++) {
+ virtio_net_init(vbus, p->tab_eth[i].net);
+ s->common.net = p->tab_eth[i].net;
+ }
+
+ if (p->files[VM_FILE_KERNEL].buf) {
+ copy_kernel(s, p->files[VM_FILE_KERNEL].buf,
+ p->files[VM_FILE_KERNEL].len,
+ p->cmdline ? p->cmdline : "");
+ }
+
+ return (VirtMachine *)s;
+}
+
+static void pc_machine_end(VirtMachine *s1)
+{
+ PCMachine *s = (PCMachine *)s1;
+ /* XXX: free all */
+ if (s->cpu_state) {
+ x86_cpu_end(s->cpu_state);
+ }
+ phys_mem_map_end(s->mem_map);
+ phys_mem_map_end(s->port_map);
+ free(s);
+}
+
+static void pc_vm_send_key_event(VirtMachine *s1, BOOL is_down, uint16_t key_code)
+{
+ PCMachine *s = (PCMachine *)s1;
+ if (s->keyboard_dev) {
+ virtio_input_send_key_event(s->keyboard_dev, is_down, key_code);
+ } else if (s->ps2_kbd) {
+ ps2_put_keycode(s->ps2_kbd, is_down, key_code);
+ }
+}
+
+static BOOL pc_vm_mouse_is_absolute(VirtMachine *s1)
+{
+ PCMachine *s = (PCMachine *)s1;
+ if (s->mouse_dev) {
+ return TRUE;
+ } else if (s->vm_mouse) {
+ return vmmouse_is_absolute(s->vm_mouse);
+ } else {
+ return FALSE;
+ }
+}
+
+static void pc_vm_send_mouse_event(VirtMachine *s1, int dx, int dy, int dz,
+ unsigned int buttons)
+{
+ PCMachine *s = (PCMachine *)s1;
+ if (s->mouse_dev) {
+ virtio_input_send_mouse_event(s->mouse_dev, dx, dy, dz, buttons);
+ } else if (s->vm_mouse) {
+ vmmouse_send_mouse_event(s->vm_mouse, dx, dy, dz, buttons);
+ }
+}
+
+struct screen_info {
+} __attribute__((packed));
+
+/* from plex86 (BSD license) */
+struct __attribute__ ((packed)) linux_params {
+ /* screen_info structure */
+ uint8_t orig_x; /* 0x00 */
+ uint8_t orig_y; /* 0x01 */
+ uint16_t ext_mem_k; /* 0x02 */
+ uint16_t orig_video_page; /* 0x04 */
+ uint8_t orig_video_mode; /* 0x06 */
+ uint8_t orig_video_cols; /* 0x07 */
+ uint8_t flags; /* 0x08 */
+ uint8_t unused2; /* 0x09 */
+ uint16_t orig_video_ega_bx;/* 0x0a */
+ uint16_t unused3; /* 0x0c */
+ uint8_t orig_video_lines; /* 0x0e */
+ uint8_t orig_video_isVGA; /* 0x0f */
+ uint16_t orig_video_points;/* 0x10 */
+
+ /* VESA graphic mode -- linear frame buffer */
+ uint16_t lfb_width; /* 0x12 */
+ uint16_t lfb_height; /* 0x14 */
+ uint16_t lfb_depth; /* 0x16 */
+ uint32_t lfb_base; /* 0x18 */
+ uint32_t lfb_size; /* 0x1c */
+ uint16_t cl_magic, cl_offset; /* 0x20 */
+ uint16_t lfb_linelength; /* 0x24 */
+ uint8_t red_size; /* 0x26 */
+ uint8_t red_pos; /* 0x27 */
+ uint8_t green_size; /* 0x28 */
+ uint8_t green_pos; /* 0x29 */
+ uint8_t blue_size; /* 0x2a */
+ uint8_t blue_pos; /* 0x2b */
+ uint8_t rsvd_size; /* 0x2c */
+ uint8_t rsvd_pos; /* 0x2d */
+ uint16_t vesapm_seg; /* 0x2e */
+ uint16_t vesapm_off; /* 0x30 */
+ uint16_t pages; /* 0x32 */
+ uint16_t vesa_attributes; /* 0x34 */
+ uint32_t capabilities; /* 0x36 */
+ uint32_t ext_lfb_base; /* 0x3a */
+ uint8_t _reserved[2]; /* 0x3e */
+
+ /* 0x040 */ uint8_t apm_bios_info[20]; // struct apm_bios_info
+ /* 0x054 */ uint8_t pad2[0x80 - 0x54];
+
+ // Following 2 from 'struct drive_info_struct' in drivers/block/cciss.h.
+ // Might be truncated?
+ /* 0x080 */ uint8_t hd0_info[16]; // hd0-disk-parameter from intvector 0x41
+ /* 0x090 */ uint8_t hd1_info[16]; // hd1-disk-parameter from intvector 0x46
+
+ // System description table truncated to 16 bytes
+ // From 'struct sys_desc_table_struct' in linux/arch/i386/kernel/setup.c.
+ /* 0x0a0 */ uint16_t sys_description_len;
+ /* 0x0a2 */ uint8_t sys_description_table[14];
+ // [0] machine id
+ // [1] machine submodel id
+ // [2] BIOS revision
+ // [3] bit1: MCA bus
+
+ /* 0x0b0 */ uint8_t pad3[0x1e0 - 0xb0];
+ /* 0x1e0 */ uint32_t alt_mem_k;
+ /* 0x1e4 */ uint8_t pad4[4];
+ /* 0x1e8 */ uint8_t e820map_entries;
+ /* 0x1e9 */ uint8_t eddbuf_entries; // EDD_NR
+ /* 0x1ea */ uint8_t pad5[0x1f1 - 0x1ea];
+ /* 0x1f1 */ uint8_t setup_sects; // size of setup.S, number of sectors
+ /* 0x1f2 */ uint16_t mount_root_rdonly; // MOUNT_ROOT_RDONLY (if !=0)
+ /* 0x1f4 */ uint16_t sys_size; // size of compressed kernel-part in the
+ // (b)zImage-file (in 16 byte units, rounded up)
+ /* 0x1f6 */ uint16_t swap_dev; // (unused AFAIK)
+ /* 0x1f8 */ uint16_t ramdisk_flags;
+ /* 0x1fa */ uint16_t vga_mode; // (old one)
+ /* 0x1fc */ uint16_t orig_root_dev; // (high=Major, low=minor)
+ /* 0x1fe */ uint8_t pad6[1];
+ /* 0x1ff */ uint8_t aux_device_info;
+ /* 0x200 */ uint16_t jump_setup; // Jump to start of setup code,
+ // aka "reserved" field.
+ /* 0x202 */ uint8_t setup_signature[4]; // Signature for SETUP-header, ="HdrS"
+ /* 0x206 */ uint16_t header_format_version; // Version number of header format;
+ /* 0x208 */ uint8_t setup_S_temp0[8]; // Used by setup.S for communication with
+ // boot loaders, look there.
+ /* 0x210 */ uint8_t loader_type;
+ // 0 for old one.
+ // else 0xTV:
+ // T=0: LILO
+ // T=1: Loadlin
+ // T=2: bootsect-loader
+ // T=3: SYSLINUX
+ // T=4: ETHERBOOT
+ // V=version
+ /* 0x211 */ uint8_t loadflags;
+ // bit0 = 1: kernel is loaded high (bzImage)
+ // bit7 = 1: Heap and pointer (see below) set by boot
+ // loader.
+ /* 0x212 */ uint16_t setup_S_temp1;
+ /* 0x214 */ uint32_t kernel_start;
+ /* 0x218 */ uint32_t initrd_start;
+ /* 0x21c */ uint32_t initrd_size;
+ /* 0x220 */ uint8_t setup_S_temp2[4];
+ /* 0x224 */ uint16_t setup_S_heap_end_pointer;
+ /* 0x226 */ uint16_t pad70;
+ /* 0x228 */ uint32_t cmd_line_ptr;
+ /* 0x22c */ uint8_t pad7[0x2d0 - 0x22c];
+
+ /* 0x2d0 : Int 15, ax=e820 memory map. */
+ // (linux/include/asm-i386/e820.h, 'struct e820entry')
+#define E820MAX 32
+#define E820_RAM 1
+#define E820_RESERVED 2
+#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */
+#define E820_NVS 4
+ struct {
+ uint64_t addr;
+ uint64_t size;
+ uint32_t type;
+ } e820map[E820MAX];
+
+ /* 0x550 */ uint8_t pad8[0x600 - 0x550];
+
+ // BIOS Enhanced Disk Drive Services.
+ // (From linux/include/asm-i386/edd.h, 'struct edd_info')
+ // Each 'struct edd_info is 78 bytes, times a max of 6 structs in array.
+ /* 0x600 */ uint8_t eddbuf[0x7d4 - 0x600];
+
+ /* 0x7d4 */ uint8_t pad9[0x800 - 0x7d4];
+ /* 0x800 */ uint8_t commandline[0x800];
+
+ uint64_t gdt_table[4];
+};
+
+#define KERNEL_PARAMS_ADDR 0x00090000
+
+static void copy_kernel(PCMachine *s, const uint8_t *buf, int buf_len,
+ const char *cmd_line)
+{
+ uint8_t *ram_ptr;
+ int setup_sects, header_len, copy_len, setup_hdr_start, setup_hdr_end;
+ uint32_t load_address;
+ struct linux_params *params;
+ FBDevice *fb_dev;
+
+ if (buf_len < 1024) {
+ too_small:
+ fprintf(stderr, "Kernel too small\n");
+ exit(1);
+ }
+ if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
+ fprintf(stderr, "Invalid kernel magic\n");
+ exit(1);
+ }
+ setup_sects = buf[0x1f1];
+ if (setup_sects == 0)
+ setup_sects = 4;
+ header_len = (setup_sects + 1) * 512;
+ if (buf_len < header_len)
+ goto too_small;
+ if (memcmp(buf + 0x202, "HdrS", 4) != 0) {
+ fprintf(stderr, "Kernel too old\n");
+ exit(1);
+ }
+ load_address = 0x100000; /* we don't support older protocols */
+
+ ram_ptr = get_ram_ptr(s, load_address);
+ copy_len = buf_len - header_len;
+ if (copy_len > (s->ram_size - load_address)) {
+ fprintf(stderr, "Not enough RAM\n");
+ exit(1);
+ }
+ memcpy(ram_ptr, buf + header_len, copy_len);
+
+ params = (void *)get_ram_ptr(s, KERNEL_PARAMS_ADDR);
+
+ memset(params, 0, sizeof(struct linux_params));
+
+ /* copy the setup header */
+ setup_hdr_start = 0x1f1;
+ setup_hdr_end = 0x202 + buf[0x201];
+ memcpy((uint8_t *)params + setup_hdr_start, buf + setup_hdr_start,
+ setup_hdr_end - setup_hdr_start);
+
+ strcpy((char *)params->commandline, cmd_line);
+
+ params->mount_root_rdonly = 0;
+ params->cmd_line_ptr = KERNEL_PARAMS_ADDR +
+ offsetof(struct linux_params, commandline);
+ params->alt_mem_k = (s->ram_size / 1024) - 1024;
+ params->loader_type = 0x01;
+#if 0
+ if (initrd_size > 0) {
+ params->initrd_start = INITRD_LOAD_ADDR;
+ params->initrd_size = initrd_size;
+ }
+#endif
+ params->orig_video_lines = 0;
+ params->orig_video_cols = 0;
+
+ fb_dev = s->common.fb_dev;
+ if (fb_dev) {
+
+ params->orig_video_isVGA = 0x23; /* VIDEO_TYPE_VLFB */
+
+ params->lfb_depth = 32;
+ params->red_size = 8;
+ params->red_pos = 16;
+ params->green_size = 8;
+ params->green_pos = 8;
+ params->blue_size = 8;
+ params->blue_pos = 0;
+ params->rsvd_size = 8;
+ params->rsvd_pos = 24;
+
+ params->lfb_width = fb_dev->width;
+ params->lfb_height = fb_dev->height;
+ params->lfb_linelength = fb_dev->stride;
+ params->lfb_size = fb_dev->fb_size;
+ params->lfb_base = FRAMEBUFFER_BASE_ADDR;
+ }
+
+ params->gdt_table[2] = 0x00cf9b000000ffffLL; /* CS */
+ params->gdt_table[3] = 0x00cf93000000ffffLL; /* DS */
+
+#ifdef USE_KVM
+ if (s->kvm_enabled) {
+ struct kvm_sregs sregs;
+ struct kvm_segment seg;
+ struct kvm_regs regs;
+
+ /* init flat protected mode */
+
+ if (ioctl(s->vcpu_fd, KVM_GET_SREGS, &sregs) < 0) {
+ perror("KVM_GET_SREGS");
+ exit(1);
+ }
+
+ sregs.cr0 |= (1 << 0); /* CR0_PE */
+ sregs.gdt.base = KERNEL_PARAMS_ADDR +
+ offsetof(struct linux_params, gdt_table);
+ sregs.gdt.limit = sizeof(params->gdt_table) - 1;
+
+ memset(&seg, 0, sizeof(seg));
+ seg.limit = 0xffffffff;
+ seg.present = 1;
+ seg.db = 1;
+ seg.s = 1; /* code/data */
+ seg.g = 1; /* 4KB granularity */
+
+ seg.type = 0xb; /* code */
+ seg.selector = 2 << 3;
+ sregs.cs = seg;
+
+ seg.type = 0x3; /* data */
+ seg.selector = 3 << 3;
+ sregs.ds = seg;
+ sregs.es = seg;
+ sregs.ss = seg;
+ sregs.fs = seg;
+ sregs.gs = seg;
+
+ if (ioctl(s->vcpu_fd, KVM_SET_SREGS, &sregs) < 0) {
+ perror("KVM_SET_SREGS");
+ exit(1);
+ }
+
+ memset(&regs, 0, sizeof(regs));
+ regs.rip = load_address;
+ regs.rsi = KERNEL_PARAMS_ADDR;
+ regs.rflags = 0x2;
+ if (ioctl(s->vcpu_fd, KVM_SET_REGS, &regs) < 0) {
+ perror("KVM_SET_REGS");
+ exit(1);
+ }
+ } else
+#endif
+ {
+ int i;
+ X86CPUSeg sd;
+ uint32_t val;
+ val = x86_cpu_get_reg(s->cpu_state, X86_CPU_REG_CR0);
+ x86_cpu_set_reg(s->cpu_state, X86_CPU_REG_CR0, val | (1 << 0));
+
+ sd.base = KERNEL_PARAMS_ADDR +
+ offsetof(struct linux_params, gdt_table);
+ sd.limit = sizeof(params->gdt_table) - 1;
+ x86_cpu_set_seg(s->cpu_state, X86_CPU_SEG_GDT, &sd);
+ sd.sel = 2 << 3;
+ sd.base = 0;
+ sd.limit = 0xffffffff;
+ sd.flags = 0xc09b;
+ x86_cpu_set_seg(s->cpu_state, X86_CPU_SEG_CS, &sd);
+ sd.sel = 3 << 3;
+ sd.flags = 0xc093;
+ for(i = 0; i < 6; i++) {
+ if (i != X86_CPU_SEG_CS) {
+ x86_cpu_set_seg(s->cpu_state, i, &sd);
+ }
+ }
+
+ x86_cpu_set_reg(s->cpu_state, X86_CPU_REG_EIP, load_address);
+ x86_cpu_set_reg(s->cpu_state, 6, KERNEL_PARAMS_ADDR); /* esi */
+ }
+
+ /* map PCI interrupts (no BIOS, so we must do it) */
+ {
+ uint8_t elcr[2];
+ static const uint8_t pci_irqs[4] = { 9, 10, 11, 12 };
+
+ i440fx_map_interrupts(s->i440fx_state, elcr, pci_irqs);
+ /* XXX: KVM support */
+ if (s->pic_state) {
+ pic2_set_elcr(s->pic_state, elcr);
+ }
+ }
+}
+
+/* in ms */
+static int pc_machine_get_sleep_duration(VirtMachine *s1, int delay)
+{
+ PCMachine *s = (PCMachine *)s1;
+
+#ifdef USE_KVM
+ if (s->kvm_enabled) {
+ /* XXX: improve */
+ cmos_update_irq(s->cmos_state);
+ delay = 0;
+ } else
+#endif
+ {
+ cmos_update_irq(s->cmos_state);
+ delay = min_int(delay, pit_update_irq(s->pit_state));
+ if (!x86_cpu_get_power_down(s->cpu_state))
+ delay = 0;
+ }
+ return delay;
+}
+
+static void pc_machine_interp(VirtMachine *s1, int max_exec_cycles)
+{
+ PCMachine *s = (PCMachine *)s1;
+#ifdef USE_KVM
+ if (s->kvm_enabled) {
+ kvm_exec(s);
+ } else
+#endif
+ {
+ x86_cpu_interp(s->cpu_state, max_exec_cycles);
+ }
+}
+
+const VirtMachineClass pc_machine_class = {
+ "pc",
+ pc_machine_set_defaults,
+ pc_machine_init,
+ pc_machine_end,
+ pc_machine_get_sleep_duration,
+ pc_machine_interp,
+ pc_vm_mouse_is_absolute,
+ pc_vm_send_mouse_event,
+ pc_vm_send_key_event,
+};