Check the FPU environment of user land processes after exec and the
authorbluhm <bluhm@openbsd.org>
Tue, 21 Aug 2018 18:35:18 +0000 (18:35 +0000)
committerbluhm <bluhm@openbsd.org>
Tue, 21 Aug 2018 18:35:18 +0000 (18:35 +0000)
proc0 kernel thread for FPU initialization values.

regress/sys/arch/amd64/fpu/LICENSE [new file with mode: 0644]
regress/sys/arch/amd64/fpu/Makefile [new file with mode: 0644]
regress/sys/arch/amd64/fpu/README [new file with mode: 0644]
regress/sys/arch/amd64/fpu/fdfl.c [new file with mode: 0644]
regress/sys/arch/amd64/fpu/fdump.c [new file with mode: 0644]
regress/sys/arch/amd64/fpu/feget.c [new file with mode: 0644]
regress/sys/arch/amd64/fpu/fenv.S [new file with mode: 0644]
regress/sys/arch/amd64/fpu/fenv_t.ok [new file with mode: 0644]
regress/sys/arch/amd64/fpu/fxproc0.c [new file with mode: 0644]
regress/sys/arch/amd64/fpu/fxsave64.ok [new file with mode: 0644]

diff --git a/regress/sys/arch/amd64/fpu/LICENSE b/regress/sys/arch/amd64/fpu/LICENSE
new file mode 100644 (file)
index 0000000..8695620
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2018 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
diff --git a/regress/sys/arch/amd64/fpu/Makefile b/regress/sys/arch/amd64/fpu/Makefile
new file mode 100644 (file)
index 0000000..359b13a
--- /dev/null
@@ -0,0 +1,61 @@
+# $OpenBSD: Makefile,v 1.1.1.1 2018/08/21 18:35:18 bluhm Exp $
+
+PROGS =                fenv fdump fdfl feget fxproc0
+SRCS_fenv =    fenv.S
+LDADD_fenv =   -nostdlib -nopie
+LDADD_fdfl =   -lm
+LDADD_feget =  -lm
+LDADD_fxproc0 =        -lkvm
+CFLAGS =       -O2 ${PIPE} ${DEBUG}
+CFLAGS +=      -Wformat -Wno-compare-distinct-pointer-types
+WARNINGS =     yes
+CLEANFILES =   *.out
+
+MODEL !=       sysctl -n hw.model | tr -c '[:alnum:]' '_'
+ALLOWKMEM !=   sysctl -n kern.allowkmem
+
+REGRESS_TARGETS +=     run-regress-fenv
+run-regress-fenv: fenv fdump
+       @echo '\n======== $@ ========'
+.if ${MODEL:C/.*_AMD_Opteron_.*/opteron/} != opteron
+       # Load FPU environment directly at _start and write it.
+       # Read FPU environment and print it to stdout.
+       ./fenv | ./fdump >fenv.out
+       diff ${.CURDIR}/fenv_t.ok fenv.out
+.else
+       # Early AMD Opteron processors behave differently.
+       @echo SKIPPED
+.endif
+
+REGRESS_TARGETS +=     run-regress-fdfl
+run-regress-fdfl: fdfl
+       @echo '\n======== $@ ========'
+       # Print default libm FPU environment to stdout.
+       ./fdfl >fdfl.out
+       diff ${.CURDIR}/fenv_t.ok fdfl.out
+
+REGRESS_TARGETS +=     run-regress-feget
+run-regress-feget: feget
+       @echo '\n======== $@ ========'
+.if ${MODEL:C/.*_AMD_Opteron_.*/opteron/} != opteron
+       # Get FPU environment via libm and print it to stdout.
+       ./feget >feget.out
+       diff ${.CURDIR}/fenv_t.ok feget.out
+.else
+       # Early AMD Opteron processors behave differently.
+       @echo SKIPPED
+.endif
+
+REGRESS_TARGETS +=     run-regress-fxproc0
+run-regress-fxproc0: fxproc0
+       @echo '\n======== $@ ========'
+.if ${ALLOWKMEM}
+       # Read FPU storage area from proc0 via /dev/mem and print it to stdout.
+       ${SUDO} ./fxproc0 >fxproc0.out
+       diff ${.CURDIR}/fxsave64.ok fxproc0.out
+.else
+       # Set sysctl kern.allowkmem=1 for additional tests.
+       @echo SKIPPED
+.endif
+
+.include <bsd.regress.mk>
diff --git a/regress/sys/arch/amd64/fpu/README b/regress/sys/arch/amd64/fpu/README
new file mode 100644 (file)
index 0000000..bbee367
--- /dev/null
@@ -0,0 +1,8 @@
+The OpenBSD test regress/lib/libm/fenv prints strange values from
+the Intel and AMD FPU using the libm function fegetenv(3).  Implement
+more detailed checks.
+
+Load the FPU enviroment directy after exec with an assembly program.
+Get the FPU default FPU status from libm.
+Load the FPU enviroment with fegetenv(3).
+Read the FPU storage area from proc0 via /dev/mem.
diff --git a/regress/sys/arch/amd64/fpu/fdfl.c b/regress/sys/arch/amd64/fpu/fdfl.c
new file mode 100644 (file)
index 0000000..7e9bf4e
--- /dev/null
@@ -0,0 +1,36 @@
+/*     $OpenBSD: fdfl.c,v 1.1.1.1 2018/08/21 18:35:18 bluhm Exp $      */
+/*
+ * Copyright (c) 2018 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+
+#include <fenv.h>
+#include <stdio.h>
+
+int
+main(int argc, char *argv[])
+{
+       size_t i;
+
+       printf("control\t%08x\n", FE_DFL_ENV->__x87.__control);
+       printf("status\t%08x\n", FE_DFL_ENV->__x87.__status);
+       printf("tag\t%08x\n", FE_DFL_ENV->__x87.__tag);
+       for (i = 0; i < nitems(FE_DFL_ENV->__x87.__others); i++)
+               printf("others[%zu]\t%08x\n", i, FE_DFL_ENV->__x87.__others[i]);
+       printf("mxcsr\t%08x\n", FE_DFL_ENV->__mxcsr);
+
+       return 0;
+}
diff --git a/regress/sys/arch/amd64/fpu/fdump.c b/regress/sys/arch/amd64/fpu/fdump.c
new file mode 100644 (file)
index 0000000..c18fc1a
--- /dev/null
@@ -0,0 +1,52 @@
+/*     $OpenBSD: fdump.c,v 1.1.1.1 2018/08/21 18:35:18 bluhm Exp $     */
+/*
+ * Copyright (c) 2018 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <fenv.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(int argc, char *argv[])
+{
+       struct {
+               fenv_t  fenv;
+               /* Try to read more and check env length. */
+               char    pad[1];
+       } buf;
+       fenv_t *fenv = &buf.fenv;
+       ssize_t len;
+       size_t i;
+
+       len = read(0, &buf, sizeof(buf));
+       if (len < 0 )
+               err(1, "read");
+       if (len != sizeof(fenv_t))
+               errx(1, "read len %zd is not size %zu", len, sizeof(fenv_t));
+
+       printf("control\t%08x\n", fenv->__x87.__control);
+       printf("status\t%08x\n", fenv->__x87.__status);
+       printf("tag\t%08x\n", fenv->__x87.__tag);
+       for (i = 0; i < nitems(fenv->__x87.__others); i++) {
+               printf("others[%zu]\t%08x\n", i, fenv->__x87.__others[i]);
+       }
+       printf("mxcsr\t%08x\n", fenv->__mxcsr);
+
+       return 0;
+}
diff --git a/regress/sys/arch/amd64/fpu/feget.c b/regress/sys/arch/amd64/fpu/feget.c
new file mode 100644 (file)
index 0000000..f28b15f
--- /dev/null
@@ -0,0 +1,41 @@
+/*     $OpenBSD: feget.c,v 1.1.1.1 2018/08/21 18:35:18 bluhm Exp $     */
+/*
+ * Copyright (c) 2018 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <fenv.h>
+#include <stdio.h>
+
+int
+main(int argc, char *argv[])
+{
+       fenv_t fenv;
+       size_t i;
+
+       if (fegetenv(&fenv))
+               err(1, "fegetenv");
+
+       printf("control\t%08x\n", fenv.__x87.__control);
+       printf("status\t%08x\n", fenv.__x87.__status);
+       printf("tag\t%08x\n", fenv.__x87.__tag);
+       for (i = 0; i < nitems(fenv.__x87.__others); i++)
+               printf("others[%zu]\t%08x\n", i, fenv.__x87.__others[i]);
+       printf("mxcsr\t%08x\n", fenv.__mxcsr);
+
+       return 0;
+}
diff --git a/regress/sys/arch/amd64/fpu/fenv.S b/regress/sys/arch/amd64/fpu/fenv.S
new file mode 100644 (file)
index 0000000..1178d15
--- /dev/null
@@ -0,0 +1,55 @@
+/*     $OpenBSD: fenv.S,v 1.1.1.1 2018/08/21 18:35:18 bluhm Exp $      */
+/*
+ * Copyright (c) 2018 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/syscall.h>
+
+       .section        .note.openbsd.ident, "a"
+       .p2align        2
+       .long           8
+       .long           4
+       .long           1
+       .ascii          "OpenBSD\0"
+       .long           0
+       .previous
+
+       .data
+env:
+       .long   1       /* __control */
+       .long   1       /* __status */
+       .long   1       /* __tag */
+       .long   1       /* __others 0 */
+       .long   1       /* __others 1 */
+       .long   1       /* __others 2 */
+       .long   1       /* __others 3 */
+mxcsr:
+       .long   1       /* __mxcsr */
+env_end:
+
+       .text
+       .align  8
+       .global _start
+_start:
+       fnstenv env
+       stmxcsr mxcsr
+       mov     $SYS_write,     %rax
+       mov     $1,             %rdi
+       mov     $env,           %rsi
+       mov     $(env_end-env), %rdx
+       syscall
+       mov     $SYS_exit,      %rax
+       mov     $0,             %rdi
+       syscall
diff --git a/regress/sys/arch/amd64/fpu/fenv_t.ok b/regress/sys/arch/amd64/fpu/fenv_t.ok
new file mode 100644 (file)
index 0000000..c4bd942
--- /dev/null
@@ -0,0 +1,8 @@
+control        ffff037f
+status ffff0000
+tag    ffffffff
+others[0]      00000000
+others[1]      00000000
+others[2]      00000000
+others[3]      ffff0000
+mxcsr  00001f80
diff --git a/regress/sys/arch/amd64/fpu/fxproc0.c b/regress/sys/arch/amd64/fpu/fxproc0.c
new file mode 100644 (file)
index 0000000..55bddb6
--- /dev/null
@@ -0,0 +1,128 @@
+/*     $OpenBSD: fxproc0.c,v 1.1.1.1 2018/08/21 18:35:18 bluhm Exp $   */
+/*
+ * Copyright (c) 2018 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <machine/fpu.h>
+#include <machine/pcb.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void __dead usage(void);
+void fenv_proc(kvm_t *, unsigned long);
+
+void __dead
+usage(void)
+{
+       fprintf(stderr, "usage: %s [-M core] [-N system]\n", getprogname());
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       char errbuf[_POSIX2_LINE_MAX];
+       char *memf, *nlistf;
+       kvm_t *kd;
+       int ch;
+       struct nlist nl[] = { { .n_name = "_proc0" }, { .n_name = NULL } };
+
+       memf = nlistf = NULL;
+       while ((ch = getopt(argc, argv, "M:N:")) != -1) {
+               switch(ch) {
+               case 'M':
+                       memf = optarg;
+                       break;
+               case 'N':
+                       nlistf = optarg;
+                       break;
+               default:
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+       if (argc)
+               usage();
+
+       kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
+       if (kd == NULL)
+               errx(1, "kvm_openfiles: %s", errbuf);
+       if (kvm_nlist(kd, nl) == -1)
+               errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+       if (nl[0].n_type == 0)
+               errx(1, "name '%s' has type %d", nl[0].n_name, nl[0].n_type);
+       fenv_proc(kd, nl[0].n_value);
+
+       if (kvm_close(kd) == -1)
+               errx(1, "kvm_close: %s", kvm_geterr(kd));
+       return 0;
+}
+
+void
+fenv_proc(kvm_t *kd, unsigned long p)
+{
+       struct proc proc;
+       struct user user;
+       struct fxsave64 *fxs = &user.u_pcb.pcb_savefpu.fp_fxsave;
+       size_t i;
+
+       if (kvm_read(kd, p, &proc, sizeof(proc)) == -1)
+               errx(1, "kvm_read proc: %s", kvm_geterr(kd));
+       if (kvm_read(kd, (u_long)proc.p_addr, &user, sizeof(user)) == -1)
+               errx(1, "kvm_read user: %s", kvm_geterr(kd));
+
+       if (fxs != &fxs->fx_fcw)
+               errx(1, "fxsave start %p, fx_fcw start %p",
+                   &fxs, &fxs->fx_fcw);
+       printf("fcw\t%04x\n", fxs->fx_fcw);
+       printf("fsw\t%04x\n", fxs->fx_fsw);
+       printf("ftw\t%02x\n", fxs->fx_ftw);
+       printf("unused1\t%02x\n", fxs->fx_unused1);
+       printf("fop\t%04x\n", fxs->fx_fop);
+       printf("rip\t%016llx\n", fxs->fx_rip);
+       printf("rdp\t%016llx\n", fxs->fx_rdp);
+       printf("mxcsr\t%08x\n", fxs->fx_mxcsr);
+       printf("mxcsr_mask\t%08x\n", fxs->fx_mxcsr_mask);
+       if (&fxs->fx_mxcsr_mask + 1 != fxs->fx_st)
+               errx(1, "fx_mxcsr_mask end %p, fx_st start %p",
+                   &fxs->fx_mxcsr_mask + 1, fxs->fx_st);
+       for (i = 0; i < nitems(fxs->fx_st); i++)
+               printf("st[%zu]\t%016llx:%016llx\n", i,
+                   fxs->fx_st[i][1], fxs->fx_st[i][0]);
+       if (&fxs->fx_st[i] != fxs->fx_xmm)
+               errx(1, "fx_st end %p, fx_xmm start %p",
+                   &fxs->fx_st[i], fxs->fx_xmm);
+       for (i = 0; i < nitems(fxs->fx_xmm); i++)
+               printf("xmm[%zu]\t%016llx:%016llx\n", i,
+                   fxs->fx_xmm[i][1], fxs->fx_xmm[i][0]);
+       if (&fxs->fx_xmm[i] != fxs->fx_unused3)
+               errx(1, "fx_xmm end %p, fx_unused3 start %p",
+                   &fxs->fx_xmm[i], fxs->fx_unused3);
+       for (i = 0; i < nitems(fxs->fx_unused3); i++)
+               printf("unused3[%zu]\t%02x\n", i, fxs->fx_unused3[i]);
+       if (&fxs->fx_unused3[i] != fxs + 1)
+               errx(1, "fx_unused3 end %p, fxsave end %p",
+                   &fxs->fx_unused3[i], fxs + 1);
+}
diff --git a/regress/sys/arch/amd64/fpu/fxsave64.ok b/regress/sys/arch/amd64/fpu/fxsave64.ok
new file mode 100644 (file)
index 0000000..b57af73
--- /dev/null
@@ -0,0 +1,129 @@
+fcw    037f
+fsw    0000
+ftw    00
+unused1        00
+fop    0000
+rip    0000000000000000
+rdp    0000000000000000
+mxcsr  00001f80
+mxcsr_mask     0000ffff
+st[0]  0000000000000000:0000000000000000
+st[1]  0000000000000000:0000000000000000
+st[2]  0000000000000000:0000000000000000
+st[3]  0000000000000000:0000000000000000
+st[4]  0000000000000000:0000000000000000
+st[5]  0000000000000000:0000000000000000
+st[6]  0000000000000000:0000000000000000
+st[7]  0000000000000000:0000000000000000
+xmm[0] 0000000000000000:0000000000000000
+xmm[1] 0000000000000000:0000000000000000
+xmm[2] 0000000000000000:0000000000000000
+xmm[3] 0000000000000000:0000000000000000
+xmm[4] 0000000000000000:0000000000000000
+xmm[5] 0000000000000000:0000000000000000
+xmm[6] 0000000000000000:0000000000000000
+xmm[7] 0000000000000000:0000000000000000
+xmm[8] 0000000000000000:0000000000000000
+xmm[9] 0000000000000000:0000000000000000
+xmm[10]        0000000000000000:0000000000000000
+xmm[11]        0000000000000000:0000000000000000
+xmm[12]        0000000000000000:0000000000000000
+xmm[13]        0000000000000000:0000000000000000
+xmm[14]        0000000000000000:0000000000000000
+xmm[15]        0000000000000000:0000000000000000
+unused3[0]     00
+unused3[1]     00
+unused3[2]     00
+unused3[3]     00
+unused3[4]     00
+unused3[5]     00
+unused3[6]     00
+unused3[7]     00
+unused3[8]     00
+unused3[9]     00
+unused3[10]    00
+unused3[11]    00
+unused3[12]    00
+unused3[13]    00
+unused3[14]    00
+unused3[15]    00
+unused3[16]    00
+unused3[17]    00
+unused3[18]    00
+unused3[19]    00
+unused3[20]    00
+unused3[21]    00
+unused3[22]    00
+unused3[23]    00
+unused3[24]    00
+unused3[25]    00
+unused3[26]    00
+unused3[27]    00
+unused3[28]    00
+unused3[29]    00
+unused3[30]    00
+unused3[31]    00
+unused3[32]    00
+unused3[33]    00
+unused3[34]    00
+unused3[35]    00
+unused3[36]    00
+unused3[37]    00
+unused3[38]    00
+unused3[39]    00
+unused3[40]    00
+unused3[41]    00
+unused3[42]    00
+unused3[43]    00
+unused3[44]    00
+unused3[45]    00
+unused3[46]    00
+unused3[47]    00
+unused3[48]    00
+unused3[49]    00
+unused3[50]    00
+unused3[51]    00
+unused3[52]    00
+unused3[53]    00
+unused3[54]    00
+unused3[55]    00
+unused3[56]    00
+unused3[57]    00
+unused3[58]    00
+unused3[59]    00
+unused3[60]    00
+unused3[61]    00
+unused3[62]    00
+unused3[63]    00
+unused3[64]    00
+unused3[65]    00
+unused3[66]    00
+unused3[67]    00
+unused3[68]    00
+unused3[69]    00
+unused3[70]    00
+unused3[71]    00
+unused3[72]    00
+unused3[73]    00
+unused3[74]    00
+unused3[75]    00
+unused3[76]    00
+unused3[77]    00
+unused3[78]    00
+unused3[79]    00
+unused3[80]    00
+unused3[81]    00
+unused3[82]    00
+unused3[83]    00
+unused3[84]    00
+unused3[85]    00
+unused3[86]    00
+unused3[87]    00
+unused3[88]    00
+unused3[89]    00
+unused3[90]    00
+unused3[91]    00
+unused3[92]    00
+unused3[93]    00
+unused3[94]    00
+unused3[95]    00